| 1 | ||
| 1 | ||
| 1 | ||
| 1 | ||
| 1 |
Welcome to the first SubMatrix programming competition. For the first problem in the series we are going to start with a classic: Fibonacci.
You can code in any language you like. Each response will be hand-scored by myself on my hardware. The scoring for these competitions will be based on (peak memory usage) * (total process time). Whichever language you choose to use, I will likely be able to test it.
Rule adjustments specific to Fibonacci: Without giving away hints, different solutions can achieve entirely different magnitudes of inputs and outputs, so they can't really be scored on the same scale. Solutions will first be scored on the highest power of 10 input they can process in under 5 seconds on my hardware. Then, within its cohort, a submission will be scored on peak memory * process runtime for the main problem.
A consumer-grade GPU will be available in the environment for those who wish to make use of it.
Create a program that reads a series of newline-separated numbers as input. The program can choose to read inputs either from stdin or from a file named input.list. The program will output the sum of the Fibonacci values associated with those inputs.
Assume the standard Fibonacci definition: F(0)=0, F(1)=1, F(2)=1, F(3)=2, F(4)=3, F(5)=5, F(n)=F(n-1)+F(n-2)
For example, given the input:
5
3
7
The output should be: 20
For the first round, the program will be fed successively larger powers of 10 (10^1, 10^2, 10^3) until it can't produce a result within 5 seconds. The last successful input will be termed the program's cohort number, or CN.
For the second round of scoring, the program will be fed 30 random numbers ranging between CN/10 and CN. Programs in the same cohort will be fed the exact same sequence. The solution will be scored by peak memory * process runtime
When submitting a solution, please let us know what level of assistance was used (e.g., AI-assistance, research, looking at others' answers, or independent). All are welcome, just be transparent. More than one solution is welcome.
Google Gemini answer in C.
#include <stdio.h>
#include <stdlib.h>
#define INITIAL_CAPACITY 1024
// Structure to cache Fibonacci values for fast lookup and to handle large 'n'
typedef struct {
long long *data;
int size;
int capacity;
} FibCache;
// Initialize the Fibonacci cache with F(0) = 0 and F(1) = 1
FibCache* create_fib_cache() {
FibCache *cache = (FibCache *)malloc(sizeof(FibCache));
cache->capacity = INITIAL_CAPACITY;
cache->size = 2;
cache->data = (long long *)malloc(cache->capacity * sizeof(long long));
cache->data[0] = 0;
cache->data[1] = 1;
return cache;
}
// Retrieve or compute the nth Fibonacci number efficiently
long long get_fibonacci(FibCache *cache, int n) {
if (n < 0) return 0; // Guard for invalid negative inputs
}
void free_fib_cache(FibCache *cache) {
if (cache) {
free(cache->data);
free(cache);
}
}
int main(void) {
FILE *input_stream = NULL;
}
Scoring:
The program was compiled with
clang -o index index.c -O4 --march=nativeThe program managed to take an input of 10 and took 0.002s to complete
Attempting higher powers of 10 lead to inaccurate results from overflow errors
Completing 30 random numbers ranging from 1-10 took 0m0.002s
This had a peak memory usage of 262144 bytes.
Final score: 524bs
But because it only managed to calculate up to F(10) it's in third place. Thank goodness AI isn't beating real programmers yet.
Thanks for setting this up! Here's my solution in Rust:
Unfortunately, the basic idea I came up with turned out to be the same as x0x7 (computation using multiplication). But, it seems that Rust with rug is quite a bit faster than Ruby. So, I was able to reach 100M within 2.87 seconds on my machine. I did come up with an optimization for the second round to reduce the amount of Fibonacci numbers computed. Instead of the half points, the closest power of 2 is used as a reference point. In my testing, this brought the time down from 14.89 to 8.95 seconds. At that point, printing 20MB to the terminal already takes a long time, so I did my tests with stdout redirected.
I didn't use AI/LLM tools.
Code:
Cargo.toml:
Cool. Give me a second to get this scored.
It handled an input value of 10000000 in 0.332s
The list of 30 values between 1000000-10000000 took 1.317s
Peek memory usage was 42401792 bytes
Final score: 53Mbs
Now I need to rescore mine because I improved the setup. Barring some huge surprise with that, I think you're winning unless someone else comes along.
Neat. It seems like our two halving implementations would've taken almost exactly the same amount of time.
I was playing around a bit with Rust parallelism to improve my solution but I didn't get anything to work well (turns out the async runtimes I used both only ran on a single thread...). But there's certainly some room for improvement there in case someone wants to try :)
Cool
Here is my solution. I came up with this speedup myself. Code below.
I noticed that when you manually expand Fibonacci that Fibonacci numbers show up in the coefficients. The deeper you do it the more steps you can skip. It could be generalized. An arbitrary n value can be chosen representing how many expansions and simplifications are done, and you can pick an n value that minimizes the inputs of the next wave.
The fact that each Fibonacci call branches to four sub-calls is mitigated by memoization and the fact that we are scaling down n rather quickly.
I have ideas for how it could be done better. But I wanted to get a solution in.
I suppose I need to score my own. It managed to handle an input value of 10000000 and responded in 0.821s. But the program will explode past that. Running it with 30 random numbers between 1000000-10000000 took 4.666s. I'll have it's memory score in just a second...
According to cgroups it had a peak memory of 81326080 bytes. Final score 362Mbs.
This is the integer it produced. https://submatrix.net/z/fib/result.txt
Edit: Results from better scoring method (and changing to the correct device). Same as used on @stuner's Rust implementation
It handled an input value of 1000000 in 0.360s
The list of 30 values between 1000000-10000000 took 2.114s
Peak memory usage was 115412992 bytes
Final score: 233Mbs