Ok I think I sort of understand what you are asking. You were looking at the output of the function as the recursion unwinds. I modified your code to print the output at each function call and here is what I get with
fibonacci(5)
.
5
4
3
2
1
2
3
2
1
The 5th fibonacci number is 5 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
#include <iostream>
using namespace std;
int fibonacci(const int fib)
{
cout << fib << endl;
if(fib==0) return 0;
else if(fib < 3) return 1;
else return fibonacci(fib-1)+fibonacci(fib-2);
}
int main()
{
cout<<"\nThe 5th fibonacci number is " << fibonacci(5) << endl;
return 0;
}
|
This is perfectly normal output of the function when using the basic recursion method for computing fibonacci. This is because each recursive call is trying to compute fib-1 and there is no way for it to remember what it had recently computed. For example in the output above, we try to compute fibonacci 5. I will now follow the recursive calls to show that the above output is what is expected.
fibonacci(5)
--->5
fibonacci(5 - 1)
--->4
fibonacci(4 - 1)
--->3
fibonacci(3 - 1)
--->2
[return 1]
1 + fibonacci(3 - 2)
--->1
[return 1 + 1]
2 + fibonacci(4 - 2)
--->2
[return 2 + 1]
3 + fibonacci(5 - 2)
--->3
3 + fibonacci(3 - 1)
--->2
[return 3 + 1]
4 + fibonacci(3 - 2)
--->1
[return 4 + 1]
--->The 5th fibonacci number is 5 |
As you can see from the output trace, there are times when the code is computing something it has previously computed [ex. fibonacci(3 -1)]. This is wasteful because it is slow and takes precious computation time. There is one way to help the function remember the previous computation and this is by using an array.
Here is a DP method for computing fibonacci:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
#include <iostream>
using namespace std;
static int fib_helper[100] = {0};
int fibonacci(const int fib)
{
cout << fib << endl;
if(fib < 3) return fib_helper[fib];
fib_helper[fib] = fibonacci(fib - 1) + fib_helper[fib - 2];
return fib_helper[fib];
}
// test program
int main()
{
fib_helper[1] = fib_helper[2] = 1;
cout<<"\nThe 5th fibonacci number is " << fibonacci(5) << endl;
return 0;
}
|
Output:
1 2 3 4 5 6
|
5
4
3
2
The 5th fibonacci number is 5
|
Now I will follow the recursive calls:
fibonacci(5)
--->5
fib[5] = fibonacci(5 - 1) + fib[3]
--->4
fib[4] = fibonacci(4 - 1) + fib[2]
--->3
fib[3] = fibonacci(3 - 1) + fib[1]
--->2
[return fib[2]]
fib[3] = 1 + 1
[return fib[3]]
fib[4] = 2 + 1
[return fib[4]]
fib[5] = 3 + 2
[return fib[5]]
The 5th fibonacci number is 5 |
Watch this vid to understand how the second method works:
http://www.youtube.com/watch?v=OQ5jsbhAv_M
With the way the function is used now, each call to fibonacci(n) will run in linear time. i.e. everytime you call it, it will compute:
fibonacci(n)
fibonacci(n - 1)
fibonacci(n - 2)
....
fibonacci(n - (n - 2))
1 2 3 4 5 6 7 8
|
// test program
int main()
{
fib_helper[1] = fib_helper[2] = 1;
cout<<"\nThe 5th fibonacci number is " << fibonacci(5) << endl;
cout<<"\nThe 6th fibonacci number is " << fibonacci(6) << endl;
return 0;
}
|
5
4
3
2
The 5th fibonacci number is 5
6
5
4
3
2
The 6th fibonacci number is 8 |
This is still good in comparison to it's previous exponential run time. But with the knowledge we now have with the DP method, this seems like more wasteful computation power since we already have the results of previous computations in the array, so there is still room for optimization of the program and here it is:
1 2 3 4 5 6 7
|
int fibonacci(const int fib)
{
cout << fib << endl;
if(fib < 3 || fib_helper[fib] > 0) return fib_helper[fib];
fib_helper[fib] = fibonacci(fib - 1) + fib_helper[fib - 2];
return fib_helper[fib];
}
|
Now notice the output:
5
4
3
2
The 5th fibonacci number is 5
6
5
The 6th fibonacci number is 8 |