Fibonacci series is a number sequence of 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 44, … It can be expressed by the formula below,
Fibonacci series grows almost as fast as the power of 2. Fn ~= 2^(0.694n).
Computation of Fibonacci Numbers
The naive approach to compute Fibonacci numbers is to recursion according to the formula of Fabonacci numbers. A C implementation is given below,
/*
a naive approach of computing fibonacci series, the computation time also satisfy fabonacci series, since
fabonacci series grows almost as fast as power of 2, the computation is expoential time
*/
#include <stdio.h>
#include <stdlib.h>
unsigned long fibo(int n) {
if (n == 0) {
return 0;
} else if (n == 1) {
return 1;
} else {
return fibo(n-1) + fibo(n-2);
}
}
int main(int argc, char **argv) {
int n;
unsigned long res;
struct timeval stTime, edTime;
n = atoi(argv[1]);
gettimeofday(&stTime, NULL);
res = fibo(n);
gettimeofday(&edTime, NULL);
printf("%lu, time takes (%u:%u)\n", res, (unsigned int)(edTime.tv_sec - stTime.tv_sec), (unsigned int)(edTime.tv_usec - stTime.tv_usec));
}
Suppose the time takes to compute fibo(n-1) and fibo(n-2) are O(n-1) and O(n-2) respectively, then the time complexity of computing fibo(n) is O(n) = O(n-1) + O(n-2). The time complexity itself follows the Fibonacci series. Since we know Fn~= 2^(0.694n), then O(n) ~=2^(0.694n). The computation is exponential. And since O(n)~=(2^0.694)^n ~=1.62^n, compute the Fn+1 takes around 1.6 times longer than computing Fn.
Compile the code using the command,
gcc -o fibo fibo.c
Below is a sample run of the program,
Figure 1. Execution of Recursive Version Program
It is not easy to observe that a Fibonacci number depends on its two previous numbers. The computation above computes some small Fibonacci numbers for many times. We can use an array to remember the previous computation. A C implementation is shown as below,
/*
use dynamic programming to remember previous computation results
this computation runs in linear time
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int n;
int i;
struct timeval stTime, edTime;
unsigned long* fibo;
n = atoi(argv[1]);
fibo = (unsigned long*) malloc((n+1)*sizeof(unsigned long));
memset(fibo, 0x00, sizeof(unsigned long)*n);
fibo[1] = 1;
gettimeofday(&stTime, NULL);
for (i = 2; i <= n; ++i) {
fibo[i] = fibo[i-1] + fibo[i-2];
}
gettimeofday(&edTime, NULL);
printf("%lu, time takes (%u:%u)\n", fibo[n], (unsigned int)(edTime.tv_sec - stTime.tv_sec), (unsigned int)(edTime.tv_usec - stTime.tv_usec));
}
Compile the code using the command below,
gcc -o fibo1 fibo1.c
A sample run is as below,
Figure 2. Execution of Dynamic Programming Version
It is not difficult to tell the code above runs in linear time. However, the code takes a lot of memory space and if we’re to compute a Fibonacci number Fn for a very large n, then we may run out of memory space.
To compute Fn, we only need values for Fn-1 and Fn-2, this leads to the code below,
/*
use dynamic programming to remember previous computation results
this computation runs in linear time
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv) {
int n;
int i;
struct timeval stTime, edTime;
unsigned long fibo[2];
n = atoi(argv[1]);
fibo[0] = 0;
fibo[1] = 1;
gettimeofday(&stTime, NULL);
for (i = 2; i <= n; ++i) {
fibo[i%2] = fibo[0] + fibo[1];
}
gettimeofday(&edTime, NULL);
if (n&0x01) {
printf("%lu, time takes (%u:%u)\n", fibo[1], (unsigned int)(edTime.tv_sec - stTime.tv_sec), (unsigned int)(edTime.tv_usec - stTime.tv_usec));
} else {
printf("%lu, time takes (%u:%u)\n", fibo[0], (unsigned int)(edTime.tv_sec - stTime.tv_sec), (unsigned int)(edTime.tv_usec - stTime.tv_usec));
}
}
Compile the code using the command below,
gcc -o fibo2 fibo2.c
A sample run is as below,
Figure 3. Execution of Dynamic Programming Version Improved
References:
1. Algorithms. Dasgupta, C.H. Papadimitriou, and U. V. Vazirani, 2006
2. Wikipedia Fibonacci Number: http://en.wikipedia.org/wiki/Fibonacci_number