Ironically, the method used above i.e. binary recursion computes the Fibonacci number by making two recursive calls in each non-base case. Unfortunately, such a direct implementation of the Fibonacci formula number in this way requires an exponential number of calls to the method.
We are tempted to use the bad recursive formulation because of the way the nth Fibonacci number, F(n), depends on the two previous values, F(n-2) and F(n-1). But notice that after computing F(n-2), the call to compute F(n-1) requires its own recursive call to compute F(n-2), as it does not have the knowledge of value of F(n-2) that was computed at the earlier level of recursion. That is duplicate work. Worse yet, both of these calls will need to (re)compute the value of F(n-3), as will the computation of F(n-1). This snowballing effect is what leads to the exponential running time of fib()
.
We can compute F(n) much more efficiently using linear recursion in which each invocation makes only one recursive call. To do so, we need to redefine the expectations of the method. Rather than having the method that returns a single value, which is the nth Fibonacci number, we define a recursive method that returns an array with two consecutive Fibonacci numbers {F(n), F(n-1)} using the convention F(-1)=0. Although it seems to be a greater burden to report two consecutive Fibonacci numbers instead of one, passing this extra information from one level of the recursion to the next makes it much easier to continue the process. (It allows us to avoid having to recompute the second value that was already known within the recursion.)
An implementation based on this strategy is clearly shown here.