Everything2
Near Matches
Ignore Exact
Full Text
Everything2

Compute Fibonacci numbers FAST!

created by ariels

(idea) by ariels (4.5 d) (print)   ?   1 C! I like it! Mon Jul 08 2002 at 11:16:03

The Fibonacci numbers are a commonly-given example in Computer Science and in Mathematics. They will undoubtably make an appearance in every single introductory programming course. It is therefore perhaps surprising that an efficient method for computing them is so little-known! Instead, we make do with methods which take time O~(n) (or even, horror of horrors, time O~(Fn)!) to compute Fn.

The recursive definition

F0=0
F1=1
Fn+1=Fn+Fn-1
leads to a straightforward implementation. Unfortunately, this implementation is horribly inefficient: to compute Fn+1, you need to compute Fn and Fn-1, and perform an extra addition. Solving the recurrence relation yields time O~(Fn) to compute Fn -- this is time exponential in n, therefore exponentially exponential in the size of the input n.

Most courses will therefore teach a somewhat faster algorithm: at any given step, keep variables x and y, with x being Fn and y being Fn+1. The loop updates by saying

x' = y
y' = x+y
(a temporary variable will often be needed to prevent one update clobbering the other). This loop requires about n iterations to compute Fn, so the time required is O~(n) -- linear in n, but exponential in the size of n.

But this algorithm too is rubbish! Binet's formula gives us a hint: by computing powers of some irrational quadratic surd, we can get the answer. And lots of people have seen how to compute the n'th power in time O~(log n). Why not apply it here, and be done in logarithmic time?

Unfortunately, this approach cannot work: you'll need to compute an indecently large number of digits of sqrt(5) (or alternatively of the golden ratio φ), which will nullify your advantages. We need to take powers of something else.

That something is the square 2x2 matrix

[[1 1]
 [1 0]]
This matrix (call it G) has the property that Gn is exactly
[[Fn+1 F n ]
 [F n  Fn-1]],
as can easily proved by induction. (People with a background in linear algebra might note that the matrix's characteristic polynomial is the familiar x2-x-1, so that its eigenvalues are precisely the golden ratio φ and its complement -φ+1/2. Really bored people with a background in linear algebra will go on to diagonalize the matrix and derive Binet's formula. Really really bored people will go on to derive the general solution for a linear recurrence from this technique.)

So, to compute Fn in time O~(log n), we need to show how to compute Gn with O(log n) matrix multiplications. But that's easy (see also fast exponentiation for an explanation of this process)!

G0 = I
G2k+1 = G2k*G
G2k = (Gk)2
So we can compute Gn efficiently; the rest is merely coding.

This is probably about the most efficient method of calculating Fibonacci numbers. It's exponentially better than "efficient" linear algorithm, and exponentially exponentially better than the naïve translation of the recurrence relation!


Note on "O~(f(n))"

This formalism, used above, will not be too familiar. It means that O(f(n)) arithmetic operations on words are required, where words are assumed to be "big enough" to hold the result. Of course this isn't true -- words have some limited size. If we wish to have unbounded n, we must replace these operations with operations on variable-width numbers: a 256-bit adder will be slower than a 32-bit adder (even if a carry lookahead adder is used); in a program, no parallelism is possible, and the slowdown is even greater. This is particularly important with number which grow exponentially with n, like the Fibonacci numbers: the number of digits required for Fn is O(n).

Still, counting arithmetic operations is easy to do, and is accurate enough for most purposes. If required, we can translate the time bounds above to absolute bounds ("time O(g(n))") by multiplying by appropriate factors. The logarithmic algorithm remains much better.


(idea) by Raspy (2.1 d) (print)   ?   1 C! I like it! Thu Feb 19 2004 at 2:22:40

If you find the matrix implementation above intimidating, here's how to get the same O(log n) behavior* using an amazing relationship between Fibonacci numbers and the Golden Ratio. The Golden Ratio, typically represented by the Greek letter phi (Φ), is defined as

Φ = (1 + 51/2) / 2,

or approximately 1.618. We'll also use the conjugate of Φ, which we'll call Φ^:

Φ^ = (1 - 51/2) / 2,

or approximately -.618. The formula for the nth Fibonacci number, then, is simply:

Fn = (Φ^n - Φn) / 51/2

We can further simplify the formula by getting rid of the conjugate, since its contribution is always pretty negligible. Because |Φ^| < 1, we know |Φ^n| / 51/2 < 1/51/2 < 1/2, so the subtracted portion will always be less than a half. Therefore we can say Fn = Φn / 51/2, rounded to the nearest integer. In other words, to get the nth Fibonacci number, all you have to do is take the Golden Ratio to the nth power, divide it by the square root of 5, and round it off. Amazing, right?

But why is any of this the case? Well, Fibonacci numbers and the Golden Ratio both occur with freakish regularity in nature, so it makes sense that they would be cosmically linked somehow. Ok, so the real answer is I have no freaking clue why**. But logic shows us things we can't necessarily grasp intuitively, and I can at least prove that it's true.

* The original version of this writeup made the somewhat smug assertion that we could get O(1) time from this "algorithm", but pfft set me straight: computing the nth power of phi still requires log n time. This is why I shouldn't do technical nodes.

** For a real explanation of the relationship between the Fibonacci numbers and the Golden Ratio, see mblase's definitive writeup under the latter.


Proof

The recursive definition of the Fibonacci sequence is:

F0 = 0
F1 = 1
Fn+2 = Fn + Fn+1 (n >= 0)

Let P(n) be the proposition that Fn = (Φn - Φ^n) / 51/2.

For n of 1 and 2: F1 is defined to be 1. (Φ1 - Φ^1) / 51/2 = 1, so P(1) is true. F2 is defined to be F0 + F1 = 0 + 1 = 1. (Φ2 - Φ^2) / 51/2 = 1, so P(2) is true as well.

Now for the inductive step. Assume P(k) and P(k+1) to be true; that is, Fk = (Φk - Φ^k) / 51/2 and Fk+1 = (Φk+1 - Φ^k+1) / 51/2. We will show that P(k+2) can be inferred from this. By definition, Fk+2 = Fk + Fk+1. Based on the above assumption, we can substitute this for the right side of the equation:

Fk+2 = (Φk - Φ^k) / 51/2 + (Φk+1 - Φ^k+1) / 51/2

With some algebraic manipulation,

Fk+2 = ((Φk - Φ^k) + (Φk+1 - Φ^k+1)) / 51/2
Fk+2 = (Φk + Φk+1 - Φ^k - Φ^k+1) / 51/2
Fk+2 = (Φk(1 + Φ) - Φ^k(1 + Φ^) / 51/2
Fk+2 = (ΦkΦ2 - Φ^kΦ^2 / 51/2 ***
Fk+2 = (Φk+2 - Φ^k+2) / 51/2

Which is exactly what we were proposing to show. Therefore (P(k) & P(k+1)) --> P(k+2) and the proof is complete.

*** Show that Φ2 = 1 + Φ:

Φ2 = ((1 + 51/2)/2)2
Φ2 = (1 + 2*51/2 + 5) / 4
Φ2 = (6 + 2*51/2) / 4
Φ2 = (3 + 51/2) / 2
Φ2 = (2 + 1 + 51/2) / 2
Φ2 = 2/2 + (1 + 51/2) / 2
Φ2 = 1 + Φ

(The same process can be used to show that Φ^2 = 1 + Φ^)


ariels says "One problem with using Binet's formula (as you do) is that in order to perform accurate calculations of φ^n/sqrt(5) you need to expand the two quadratic surds (φ and sqrt(5)) to increasing precisions. I think you run out of precision quite quickly with floats or even doubles..."

(idea) by Olathe (1.7 y) (print)   ?   I like it! Sat Sep 09 2006 at 0:34:30

I've tried all the methods above, but the method used by the GNU Multiple Precision (GMP) arithmetic library is at least five times faster than its nearest competitor in Ruby. Because GMP is optimized for speed and written in C, I assume the method works quickly in other languages as well.

To find the nth Fibonacci number, use the following recursive definition :

  • F0 = 0
  • F1 = 1
  • When n is an even number :
       Fn = Fn / 2(Fn / 2 + 2 F(n−2) / 2)
  • When n, modulo 4, is 1 :
       Fn = (2 F(n−1) / 2 + F(n−3) / 2)(2 F(n−1) / 2 − F(n−3) / 2) + 2
  • When n, modulo 4, is 3 :
       Fn = (2 F(n−1) / 2 + F(n−3) / 2)(2 F(n−1) / 2 − F(n−3) / 2) − 2


Here's the Ruby code (using a hash table for memoization) :

class Integer
  FibonacciComputer = Hash.new do |fibonacci, n|
    if fibonacci.has_key?(n - 1) and fibonacci.has_key?(n - 2)
      fibonacci[n] = fibonacci[n - 1] + fibonacci[n - 2]
    elsif fibonacci.has_key?(n + 1) and fibonacci.has_key?(n + 2)
      fibonacci[n] = fibonacci[n + 2] - fibonacci[n + 1]
    else
      half_n = n.div(2)
      case n.modulo(4)
        when 1
          fibonacci[n] = (2*fibonacci[half_n] + fibonacci[half_n - 1])*(2*fibonacci[half_n] - fibonacci[half_n - 1]) + 2
        when 3
          fibonacci[n] = (2*fibonacci[half_n] + fibonacci[half_n - 1])*(2*fibonacci[half_n] - fibonacci[half_n - 1]) - 2
        else
          fibonacci[n] = fibonacci[half_n]*(fibonacci[half_n] + 2*fibonacci[half_n - 1])
      end
    end
  end
  FibonacciComputer[0] = 0
  FibonacciComputer[1] = 1
  FibonacciComputer.freeze
  
  def fib
    return FibonacciComputer.dup[self]
  end
end

puts "The 1,000th Fibonacci number is #{ 1_000.fib }."

Try it out at http://www.ruby.ch/interpreter/rubyinterpreter.shtml.


printable version
chaos

Make money fast Golden ratio Fibonacci number fast exponentiation
C++: computing Fibonacci numbers at compile time Perrin numbers matrix multiplication 001-01-0001
Big-oh notation Business plan carry lookahead adder Surd
Memoization eigenvalue Powers of matrices Binet's formula
linear recurrence first order linear recurrence quadratic formula computer science
recurrence relation Woohoo hash table Ruby
Y'know, if you log in, you can write something here, or contact authors directly on the site. Create a New User if you don't already have an account.
  Epicenter
Login
Password

password reminder
register

Everything2 Help

Cool Staff Picks
Nodes your sibling would have liked:
Kiss of the Spider Woman
A Flock of Seagulls
Hamlet Chicken Plant Disaster
Treatise on Time (You Fucko)
Entrust A Letter
Being a foreign female in Japan
The Everything2 Podcast
postmodern Japan
spleen
Dragging a disk to the trash to eject it on a Macintosh
I Can't Make You Love Me
test5
The White Butterfly
New Writeups
everyday j.Lo
pray do not molest them(thing)
ammie
Bands Who Take Their Names from Eighteenth-century English Poetry and Prose(idea)
shaogo
Under My Thumb(review)
ammie
Rock On(person)
The Custodian
The Dresden Files(thing)
Ouzo
PETA becomes you, a proposed future(fiction)
Ereneta
Stone Soup, Part Two(fiction)
jjen
Sorrier than I ever thought I would be(personal)
locke baron
Moskva class antisubmarine cruiser(thing)
Wuukiee
May 15, 2008(idea)
locke baron
Kuznetsov class aircraft carrier(thing)
Adaptive Child
Annie's garden salsa(recipe)
Simulacron3
Zig-Zag(thing)
Noung
Tiananmen Square Massacre(idea)
aneurin
Lord St Clair(person)
Everything 2 is brought to you by the letter C and The Everything Development Company