Calculating Fibonnacci Numbers
Explore the calculation of Fibonacci numbers through classic recursion, understand the exponential time complexity via recurrence relations, and learn how dynamic programming techniques such as memoization reduce redundant computations to optimize performance.
Classic Recursive Implementation of The Fibonacci Series
Before we dive into what dynamic programming is, let’s have a look at a classic programming problem Fibonacci Series. You have probably already seen it, but let’s start with a quick refresher. The Fibonacci series is a series of numbers in which each number is the sum of the preceding two numbers. The first two numbers are 0 and 1. So it looks like:
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, …
Here is a C++ function that returns nth the Fibonacci number.
Time Complexity with Recurrence Relations
To calculate the time complexity of the code, we can solve a recurrence relation,
represents the fib function. and represent the recursive calls to fib(n-1) fib(n-2), whereas the ‘4’ represents the basic operations in the code. Namely, the comparison on line 2 and the two subtractions and one addition on line 4. Lastly, and represent the base-cases.
To solve this recurrence, we can simplify it by approximating that . The time that is taken to calculate is greater than the time taken to calculate , so this approximation gives us an upper bound on the total time complexity,
eq 1:
Also by this relation,
eq 2: where
So we can replace in eq 1 with the value of it in eq 2 like so to get,
You must have noticed a pattern here, which means that this equation can be written in generic terms.
generic:
where is the number of times that the equation was substituted into plus one. Have a look at the following for a clearer picture,
We can now put the generic equation in terms of the value of which we know is . This can be done by setting equal to , which would mean,
We can substitute the value of which is into the equation to get,
Hence,
This means that the fib(n) function is in —i.e. exponential time—which isn’t very efficient. What can be causing this and how can we make this better?
Redundant Calculations
One factor is the fact that some terms of fib(n) are calculated several times. Look at the following slides to see which ones are repeated in the case of fib(6),
Turns out that these redundant calculations can be eliminated which would reduce the time complexity of the algorithm significantly using a technique called memoization.