Is there a noticeable computation time difference between recursion-style Fibonacci vs. loop-style Fibonacci? I keep running Fibonacci to 40 places using recursion and then using a loop directly afterwards. It seems as though the computation time difference is only academic.
Written in C
Recursive solution:
int main(int argc, const char * argv[]) {
int n, i = 0, c;
printf("Please enter an integer:\n");
scanf("%d", &n);
for ( c = 1 ; c <= n ; c++ )
{
printf("%lu ", fibonacci(i));
i++;
}
return 0;
}
long fibonacci(long n)
{
if ( n == 0 )
return 0;
else if ( n == 1 )
return 1;
else
return ( fibonacci(n-1) + fibonacci(n-2) );
};
For-loop solution:
int main(int argc, const char * argv[]) {
int n, first = 0, second = 1, next, c;
printf("Please enter an integer:\n");
scanf("%d", &n);
for ( c = 0 ; c < n ; c++ )
{
if ( c <= 1 )
next = c;
else
{
next = first + second;
first = second;
second = next;
}
printf("%d ",next);
}
return 0;
};
The conventional recursion method is extremely slow compared to the tail recursive and iterative versions. In the example code below for the iteration version uses an unfolded loop along with Duff's Device to enter the loop. For 32 bit unsigned integers, the limit is fib(47), for 64 bit unsigned integers, the limit is fib(93).
Timing was done with Intel 2600K 3.4ghz, XP X64, 64 bit mode. XP or XP X64 high performance counter frequency is the same as the cpu clock, 3.4ghz, but operating system overhead (like interrupts), affects the timing if the duration is small.
Timings for fib(40):
fibr() # of microseconds 485468.8
fibt() # of microseconds 0.2
fibi() # of microseconds 0.2
Timing for 94 loops, n = 0 to 93:
fibt() # of microseconds 7
fibi() # of microseconds 5
Example code:
typedef unsigned long long UI64;
UI64 fibr(UI64 n)
{
if(n < 2)
return n;
return fibr(n-1) + fibr(n-2);
}
// call with fibt(n, 0, 1)
UI64 fibt(UI64 n, UI64 res, UI64 next)
{
if (n == 0)
return res;
return fibt(n - 1, next, res + next);
}
UI64 fibi(UI64 n)
{
UI64 f0, f1, i;
if(n < 2)
return n;
n -= 2;
f1 = f0 = 1;
i = 0;
switch(n%8){
do{
f1 += f0;
case 7:
f0 += f1;
case 6:
f1 += f0;
case 5:
f0 += f1;
case 4:
f1 += f0;
case 3:
f0 += f1;
case 2:
f1 += f0;
case 1:
f0 += f1;
case 0:
continue;
}while(n >= (i += 8));
}
return f0;
}
Alternate version of fibi(), without the n<2 check. What f0 and f1 represent changes within the loop designed to end up with the final sum in f0, so the initial state of what f0 and f1 represent depends if n is even or odd. If n is even, f0 = fib(0) = 0, f1 = fib(-1) = 1, if n is odd, f1 = fib(0) = 0, f0 = fib(-1) = 1 . (In case you're curious, fib(-1) = 1, fib(-2) = -1, fib(-3) = 2, fib(-4) = -3, fib(-5) = 5, fib(-6) = -8, ... ).
To explain the logic here, for the n even case, fib(-1) = f1 = 1, fib(0) = f0 = 0, then fib(1) = (f1 += f0), fib(2) = (f0 += f1), fib(3) = (f1 += f0), fib(4) = (f0 += f1), ... .
UI64 fibi(UI64 n)
{
UI64 f0, f1, i;
f0 = n & 1; // if n even, f0=0, f1=1
f1 = 1 - f0; // else f1=0, f0=1
i = 0;
switch(n%8){
do{
f1 += f0;
case 7:
f0 += f1;
case 6:
f1 += f0;
case 5:
f0 += f1;
case 4:
f1 += f0;
case 3:
f0 += f1;
case 2:
f1 += f0;
case 1:
f0 += f1;
case 0:
continue;
}while(n >= (i += 8));
}
return f0;
}
A for-loop it is not necessarily faster. In general languages like Java, C, and Python, recursion is fairly expensive compared to iteration because it requires the allocation of a new stack frame.
It is possible to eliminate this overhead in C/C++ enabling compiler optimization to perform tail recursion, which transforms certain types of recursion (actually, certain types of tail calls) into jumps instead of function calls. To let the compiler performs this optimization it is necessary that the last thing a function does before it returns is call another function (in this case itself).
An example of Fibonacci function could be like this:
int fib_tail(int n, int res, int next)
{
if (n == 0) {
return res;
}
return fib_tail(n - 1, next, res + next);
}
and at assembly level, enabling compiler optimization, it will be implemented as a loop, e.g. sharing the same stack frame between calls.
I recently wrote an article about it.
Hope it helps.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With