pspace.org

  • Increase font size
  • Default font size
  • Decrease font size

Comparing Fibonacci algorithms

E-mail Print PDF

An often used example for worst, average and best case algorithm analysis in computer science are the Fibonacci numbers, which are defined as fib(n) = fib(n-1) + fib(n-2) with fib(1) = fib(2) = 1. Often a recursive and an iterative algorithm are given to analyze the different performances, with the result that recursions are always bad and should be avoided. I want to show that this is not always true.

The given recursive algorithm generally looks like this:

int fib(n):
return fib(n-1) + fib(n-2)

While the iterative approach may be similar to this one:

int fib(n):
f = f1 = f2 = 1
for i=3 to n:
f = f1+f2
f2 = f1
f1 = f
return f

The question is now, which one is better regarding time consumption. The answer is obvious; while this recursive one starts two sub calls for each n and calculates values doubly, which ends up with O(2n) in Landau notation, the iterative one just calculates the next value based on the predecessors, what would make O(n). The usage of this single sample over and over again gives the impression, that recursions are always baaaad. But with a few tricks we will be able to change that (I must admit that they are only possible with a higher memory usage).

The biggest problem about the recursive solution above is that every call of fib(n-2) will calculated a Fibonacci number, we already know. Let's have a closer look on this. When trying to give us the fifth Fibonacci number, we would do the following:

fib(5) = fib(4)                    + fib(3)
       = fib(3) +          fib(2)  + fib(3)
       = fib(2) + fib(1) + fib(2)  + fib(3)
       = 1      + 1      + 1       + fib(2) + fib(1)
       = 5

In this example, fib(3) is calculated twice, but we only need to do this once, as long space time does not rip and change mathematics. So we use a simple buffer of the size n to cache all our results, and when we calculated a Fibonacci number, we store it in there. With this little trick, we can reduce the algorithm to O(n), just like the iterative one! The code might look like this:

int[n] buffer = {0,0...,0}
int fib(n):
  if buffer[n] != 0:  //if we calculated that one before, we can skip here.
    return buffer[n]
  else
    return buffer[n] = fib(n-1)+fib(n-2)

Please note that this trick cannot be done with the iterative code, as it does not depend on predecessors of n except of the two it stores itself in f1 and f2.
But we can go even further. For that, let's just expand the formal definition a bit by just plugging itself for n=7.

fib(7) = fib(6)          + fib(5)
= fib(5) + fib(4) + fib(5)
       = 2*fib(5) + fib(4)
       = 3*fib(4) + 2*fib(3)
       = 5*fib(3) + 3*fib(2)
       = 8*fib(2) + 5*fib(1)
       = 8 + 5 = 13

You will notice, that the factors are increasing Fibonacci numbers, while the others decrease. We can use this to meet just in the middle:

fib(n) = { n is odd:  fib((n+1)/2)² + fib((n-1)/2)²
          n is even: fib(n/2) * (fib(n/2+1) + fib(n/2-1)) }

With this we can reduce the whole recursion to O(n), as we need approx fib(n/2) at every call, which gives us log(n) as depth. As we still need about 2 new calls for each level, we get O(2log n) = O(n). If we add a buffer, we get even more. We then need only 2 (or 3) new calls for every sub call, which are buffered and gives us, summed up, O(log² n).

In the final competition between iterative, recursive with buffer and this own, modified recursive algorithm, the winner for the Fibonacci numbers up to 10000 is clear: (The fact that they do not look exactly look like O(n) and O(log² n) are artefacts from the BigInteger-class I was using, as fib(10000) is a very big number)

Last Updated on Monday, 09 November 2009 22:40  

Most recent entries

Fun with Sturm chains

22/01/2012 | 3_of_8 I recently did a seminar on decision procedures, and my talk was on Tarski's decision procedure for first-order real arithmetic. Basically, the problem is that you have a formula with real variables ...
More...

Distributed Hash Tables (DHT) in Peer to Peer Networks (German)

17/12/2011 | inherited Two years ago, I had to write a paper like thing at school on any subject, so we (me and a classmate) chose Distributed Hash tables in Peer to Peer Networks. Long story short, they are used to accel...
More...

The Untyped Lambda Calculus

19/08/2011 | 3_of_8 I just remembered a talk I did on Alonzo Church's Untyped Lambda Calculus earlier this year. (they call it a "Proseminar") The Lambda Calculus is a very simple but turing-complete functional model ...
More...

Protocols of Physics Lab Course at University of Göttingen (German)

01/08/2011 | inherited Here's a list of our physics lab course protocols (http://www.praktikum.physik.uni-goettingen.de), which we started at summer semester 2011 in Göttingen. The list is incomplete, as not all of the p...
More...

3D fieldlines

12/05/2011 | inherited For a physics project at university, I had to calculate and plot fieldlines in three dimensions. I wrote a little tool to do the math and show them with OpenGL. I will publish it soon, until then, hav...
More...