Preface
Writing a method to generate the nth Fibonacci number is not a rocket science. The recursive formula for that is very simple and can be written following:
1 2 3 |
|
The n-th Fibonacci number is just the sum of two previous Fibonacci numbers and the first and second formula are our ‘base cases’. Based on this we can write a method public static long slowFibonacci(int n)
1 2 3 4 5 6 7 8 9 |
|
You have already noticed that instead of writing fibonacci I have written slowFibonacci. There is a reason for that and You may guessing that probably we can do something with this method to make it much faster and You have right. There is a quite usful programming method which we can use to improve the performance of this method. However before doing this let’s try to write a method call stack trace for let’s say 5th Fibonacci number.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
The number of dashes means how many delayed operations is on the stack. Dash followed by the ‘>’ (‘->’) means that the operation can be computed (return value is provided) and removed from the stack.
You can notice that the same operations are evaluated many times, for example slowFibonacci(2)
is computed 3 times. It is obvious waste of cpu resources. What can we do to use previously computed value instead of evaluating it again and again ?
Dynamic programming
‘Dynamic programming’ method comes to our rescue. According to the wiki, ‘dynamic programming’ is a method for solving a complex problem by breaking it down into a collection of simpler subproblems. It is applicable to problems exhibiting the properties of overlapping subproblems and optimal substructure. The dynamic programming approach seeks to solve each subproblem only once, thus reducing the number of computations: once the solution to a given subproblem has been computed, it is stored or “memoized”: the next time the same solution is needed, it is simply looked up.
What does it mean for us ? Each already solved subproblem (computed i-th Fibonacci number) can be saved in the let’s say global array and if the same solution is needed just simply look for it in that table.
Coding
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
Call stack trace for this tuned method is following
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
You can notice that the number of calls is much smaller then for slowFibonacci(5)
.
At the end I would like to present a simple benchmark
1 2 3 4 5 6 |
|
To compute 45-th Fibonacci number for fibonacci
it takes 2ms and for slowFibonacci
it takes 7.8s so a savings are significant.