4

As we all know, the simplest algorithm to generate Fibonacci sequence is as follows:

if(n<=0) return 0;
else if(n==1) return 1;
f(n) = f(n-1) + f(n-2);

But this algorithm has some repetitive calculation. For example, if you calculate f(5), it will calculate f(4) and f(3). When you calculate f(4), it will again calculate both f(3) and f(2). Could someone give me a more time-efficient recursive algorithm?

Derek Mahar
  • 27,608
  • 43
  • 124
  • 174
chaonextdoor
  • 5,019
  • 15
  • 44
  • 61
  • 1
    `f(n - 1) = f(n - 2) + f(n - 3)` so `f(n) = 2 * f(n - 2) + f(n - 3)`. You can cache `f(n - 2)`. Of course, doing it iteratively is much better, especially if your language of choice has `yield`. – Ry- Jun 07 '12 at 00:33
  • @minitech if I want to use the cache method, can you give me the complete code? – chaonextdoor Jun 07 '12 at 03:04
  • @minitech Is javascript ok with you? – chaonextdoor Jun 07 '12 at 16:15

9 Answers9

5

I have read about some of the methods for calculating Fibonacci with efficient time complexity following are some of them -

Method 1 - Dynamic Programming Now here the substructure is commonly known hence I'll straightly Jump to the solution -

static int fib(int n) 
{ 
int f[] = new int[n+2]; // 1 extra to handle case, n = 0 
int i; 

f[0] = 0; 
f[1] = 1; 

for (i = 2; i <= n; i++) 
{ 
    f[i] = f[i-1] + f[i-2]; 
} 

return f[n]; 
}

A space-optimized version of above can be done as follows -

static int fib(int n) 
 { 
    int a = 0, b = 1, c; 
    if (n == 0) 
        return a; 
    for (int i = 2; i <= n; i++) 
    { 
        c = a + b; 
        a = b; 
        b = c; 
    } 
    return b; 
} 

Method 2- ( Using power of the matrix {{1,1},{1,0}} )

This an O(n) which relies on the fact that if we n times multiply the matrix M = {{1,1},{1,0}} to itself (in other words calculate power(M, n )), then we get the (n+1)th Fibonacci number as the element at row and column (0, 0) in the resultant matrix. This solution would have O(n) time.

The matrix representation gives the following closed expression for the Fibonacci numbers: fibonaccimatrix

static int fib(int n) 
{ 
int F[][] = new int[][]{{1,1},{1,0}}; 
if (n == 0) 
    return 0; 
power(F, n-1); 

return F[0][0]; 
} 

/*multiplies 2 matrices F and M of size 2*2, and 
puts the multiplication result back to F[][] */
static void multiply(int F[][], int M[][]) 
{ 
int x = F[0][0]*M[0][0] + F[0][1]*M[1][0]; 
int y = F[0][0]*M[0][1] + F[0][1]*M[1][1]; 
int z = F[1][0]*M[0][0] + F[1][1]*M[1][0]; 
int w = F[1][0]*M[0][1] + F[1][1]*M[1][1]; 

F[0][0] = x; 
F[0][1] = y; 
F[1][0] = z; 
F[1][1] = w; 
} 

/*function that calculates F[][] raise to the power n and puts the 
result in F[][]*/
static void power(int F[][], int n) 
{ 
int i; 
int M[][] = new int[][]{{1,1},{1,0}}; 

// n - 1 times multiply the matrix to {{1,0},{0,1}} 
for (i = 2; i <= n; i++) 
    multiply(F, M); 
} 

This can be optimized to work in O(Logn) time complexity. We can do recursive multiplication to get power(M, n) in the previous method.

static int fib(int n) 
{ 
int F[][] = new int[][]{{1,1},{1,0}}; 
if (n == 0) 
    return 0; 
power(F, n-1); 

return F[0][0]; 
} 

static void multiply(int F[][], int M[][]) 
{ 
int x =  F[0][0]*M[0][0] + F[0][1]*M[1][0]; 
int y =  F[0][0]*M[0][1] + F[0][1]*M[1][1]; 
int z =  F[1][0]*M[0][0] + F[1][1]*M[1][0]; 
int w =  F[1][0]*M[0][1] + F[1][1]*M[1][1]; 

F[0][0] = x; 
F[0][1] = y; 
F[1][0] = z; 
F[1][1] = w; 
} 

static void power(int F[][], int n) 
{ 
if( n == 0 || n == 1) 
  return; 
int M[][] = new int[][]{{1,1},{1,0}}; 

power(F, n/2); 
multiply(F, F); 

if (n%2 != 0) 
   multiply(F, M); 
} 

Method 3 (O(log n) Time) Below is one more interesting recurrence formula that can be used to find nth Fibonacci Number in O(log n) time.

If n is even then k = n/2: F(n) = [2*F(k-1) + F(k)]*F(k)

If n is odd then k = (n + 1)/2 F(n) = F(k)*F(k) + F(k-1)*F(k-1) How does this formula work? The formula can be derived from the above matrix equation. fibonaccimatrix

Taking determinant on both sides, we get (-1)n = Fn+1Fn-1 – Fn2 Moreover, since AnAm = An+m for any square matrix A, the following identities can be derived (they are obtained from two different coefficients of the matrix product)

FmFn + Fm-1Fn-1 = Fm+n-1

By putting n = n+1,

FmFn+1 + Fm-1Fn = Fm+n

Putting m = n

F2n-1 = Fn2 + Fn-12

F2n = (Fn-1 + Fn+1)Fn = (2Fn-1 + Fn)Fn (Source: Wiki)

To get the formula to be proved, we simply need to do the following If n is even, we can put k = n/2 If n is odd, we can put k = (n+1)/2

public static int fib(int n) 
{ 

    if (n == 0) 
        return 0; 

    if (n == 1 || n == 2) 
        return (f[n] = 1); 

    // If fib(n) is already computed 
    if (f[n] != 0) 
        return f[n]; 

    int k = (n & 1) == 1? (n + 1) / 2 
                        : n / 2; 

    // Applyting above formula [See value 
    // n&1 is 1 if n is odd, else 0. 
    f[n] = (n & 1) == 1? (fib(k) * fib(k) +  
                    fib(k - 1) * fib(k - 1)) 
                   : (2 * fib(k - 1) + fib(k))  
                   * fib(k); 

    return f[n]; 
} 

Method 4 - Using a formula In this method, we directly implement the formula for the nth term in the Fibonacci series. Time O(1) Space O(1) Fn = {[(√5 + 1)/2] ^ n} / √5

static int fib(int n) { 
double phi = (1 + Math.sqrt(5)) / 2; 
return (int) Math.round(Math.pow(phi, n)  
                    / Math.sqrt(5)); 
} 

Reference: http://www.maths.surrey.ac.uk/hosted-sites/R.Knott/Fibonacci/fibFormula.html

thealchemist
  • 421
  • 4
  • 12
3

Look here for implementation in Erlang which uses formula \begin{pmatrix}1&1\\1&0\end{pmatrix}^{n-1}=\begin{pmatrix}\mathrm{fib}(n)&\mathrm{fib}(n-1)\ \mathrm{fib}(n-1)&\mathrm{fib}(n-2)\end{pmatrix} . It shows nice linear resulting behavior because in O(M(n) log n) part M(n) is exponential for big numbers. It calculates fib of one million in 2s where result has 208988 digits. The trick is that you can compute exponentiation in O(log n) multiplications using (tail) recursive formula (tail means with O(1) space when used proper compiler or rewrite to cycle):

% compute X^N
power(X, N) when is_integer(N), N >= 0 ->
    power(N, X, 1).

power(0, _, Acc) ->
    Acc;
power(N, X, Acc) ->
    if N rem 2 =:= 1 ->
            power(N - 1,   X,     Acc * X);
        true ->
            power(N div 2, X * X, Acc)
    end.

where X and Acc you substitute with matrices. X will be initiated with \begin{pmatrix}1&1\1&0\end{pmatrix} and Acc with identity I equals to \begin{pmatrix}1&0\0&1\end{pmatrix}.

Community
  • 1
  • 1
Hynek -Pichi- Vychodil
  • 26,174
  • 5
  • 52
  • 73
2

One simple way is to calculate it iteratively instead of recursively. This will calculate F(n) in linear time.

def fib(n):
    a,b = 0,1
    for i in range(n):
        a,b = a+b,a
    return a
Jimmy
  • 89,068
  • 17
  • 119
  • 137
  • There is way how to compute in `O(M(n)log n)` where `M(n)` is multiplication cost. Your code is actually `O(n^2)` for big numbers. See mine [answer](http://stackoverflow.com/a/20908253/49197) for fast and accurate computation for big `n`. Anyway you have to use some big numbers library or language with this support. – Hynek -Pichi- Vychodil Jan 04 '14 at 09:18
1

Hint: One way you achieve faster results is by using Binet's formula:

Here is a way of doing it in Python:

from decimal import *

def fib(n):
    return int((Decimal(1.6180339)**Decimal(n)-Decimal(-0.6180339)**Decimal(n))/Decimal(2.236067977))
Dair
  • 15,910
  • 9
  • 62
  • 107
  • 2
    Binet's formula as you presented it is not accurate for big `n` due rounding error in your constants as `1.6180339`. See mine [answer](http://stackoverflow.com/a/20908253/49197) for fast and accurate computation for big `n`. – Hynek -Pichi- Vychodil Jan 04 '14 at 09:15
1

you can save your results and use them :

public static long[] fibs;

public long fib(int n) {
    fibs = new long[n];
    return internalFib(n);
}
public long internalFib(int n) {
    if (n<=2) return 1;
    fibs[n-1] = fibs[n-1]==0 ? internalFib(n-1) : fibs[n-1];
    fibs[n-2] = fibs[n-2]==0 ? internalFib(n-2) : fibs[n-2];
    return fibs[n-1]+fibs[n-2];
}
Ofek Ron
  • 8,354
  • 13
  • 55
  • 103
0

F(n) = (φ^n)/√5 and round to nearest integer, where φ is the golden ratio....

φ^n can be calculated in O(lg(n)) time hence F(n) can be calculated in O(lg(n)) time.

phoenyx
  • 119
  • 3
0
// D Programming Language 

void vFibonacci ( const ulong X, const ulong Y, const int Limit ) {    
       // Equivalent : if ( Limit != 10 ). Former ( Limit ^ 0xA ) is More Efficient However.
       if ( Limit ^ 0xA ) {    
           write ( Y, " " ) ;
           vFibonacci ( Y, Y + X, Limit + 1 ) ;
       } ;
} ;

// Call As    
// By Default the Limit is 10 Numbers
vFibonacci ( 0, 1, 0 ) ;
Dewang
  • 1
  • 1
  • 1
    Some plain language explanation of code is almost always beneficial for the clarity of answers. – Thomas Nov 22 '14 at 20:41
0

EDIT: I actually think Hynek Vychodil's answer is superior to mine, but I'm leaving this here just in case someone is looking for an alternate method.

I think the other methods are all valid, but not optimal. Using Binet's formula should give you the right answer in principle, but rounding to the closest integer will give some problems for large values of n. The other solutions will unnecessarily recalculate the values upto n every time you call the function, and so the function is not optimized for repeated calling.

In my opinion the best thing to do is to define a global array and then to add new values to the array IF needed. In Python:

import numpy

fibo=numpy.array([1,1])
last_index=fibo.size

def fib(n):
    global fibo,last_index
    if (n>0):
        if(n>last_index):
            for i in range(last_index+1,n+1):
                fibo=numpy.concatenate((fibo,numpy.array([fibo[i-2]+fibo[i-3]])))
            last_index=fibo.size
        return fibo[n-1]
    else:
        print "fib called for index less than 1"
        quit()

Naturally, if you need to call fib for n>80 (approximately) then you will need to implement arbitrary precision integers, which is easy to do in python.

-1

This will execute faster, O(n)

def fibo(n):
    a, b = 0, 1
    for i in range(n):
        if i == 0:
            print(i)
        elif i == 1:
            print(i)
        else:
            temp = a
            a = b
            b += temp
    print(b)
n = int(input())
fibo(n)