Llaisdy
15 Jul 2024

Calculating Fibonacci numbers: a non-recursive formula

I recently discovered that there is a non-recursive formula for calculating Fibonacci numbers. fibn below is a naive implementation in Python (with fibr as a matching naive implementation of the recursive algorithm):

from math import sqrt

def fibn(x):
    sqrt5 = sqrt(5)
    r = (1 + sqrt5) / 2.0
    s = (1 - sqrt5) / 2.0
    result = ( (r ** x) - (s ** x) ) / sqrt5
    return int(result)

def fibr(x):
    if x == 1: return 1
    if x == 2: return 1
    return fibr(x-1) + fibr(x-2)

Remarkably result in fibn~ is a whole number. It has to be implemented as a float because of the √5s.

The non-recursive version is a lot faster, even for smallish numbers, and makes larger fibonacci numbers accessible. eg: fibn(20) is more than 800 times faster than fibr(20); fibn(200) returned immediately – I stopped fibr(200) after five minutes.

>>> proc.fibn(20)
6765
>>> proc.fibr(20)
6765
>>> 
>>> t_no_rec = timeit.timeit('proc.fibn(20)', setup='import proc', number=100)
>>> t_yes_rec = timeit.timeit('proc.fibr(20)', setup='import proc', number=100)
>>> 
>>> t_yes_rec / t_no_rec
844.0452218686785
>>> 
>>> proc.fibn(200)
280571172992512015699912586503521287798784

I picked up the formula in Combinatorics: a very short introduction, which is a fun read.

Unlike the recursive version, the non-recursive formula could be a continuous function, ie take floats as input. However, s is a negative number (-0.6180339887498949), so fractional powers of this will be complex numbers.

It might be interesting to prove that the formula always returns whole numbers – given integer input, but that's a story for another day.

updates (2024-12-08)

Here's the function in Haskell:

fib :: Integer -> Integer
fib x = floor (((r ** y) - (s ** y)) / sqrt5)
  where
    y = fromIntegral x
    sqrt5 = sqrt (5 :: Double)
    r = (1 + sqrt5) / 2.0
    s = (1 - sqrt5) / 2.0

The formula is known as Binet's formula. r is phi (the golden ratio) and s is Phi.

The formula is only accurate up to around 70 (fib 70 = 190392490709135), after which the fact that we're using limited precision floating point numbers starts causing errors.

Tags: maths python haskell