views:

2137

answers:

8

Is there any algorithm to compute the nth fibonacci number in sub linear time?

+5  A: 

Wikipedia has a closed form solution http://en.wikipedia.org/wiki/Fibonacci_number

Or in c#:

    public static int Fibonacci(int N)
    {
        double sqrt5 = Math.Sqrt(5);
        double phi = (1 + sqrt5) / 2.0;
        double fn = (Math.Pow(phi, N) - Math.Pow(1 - phi, N)) / sqrt5;
        return (int)fn;
    }
JDunkerley
You can avoid the need to compute to two exponentials by using the fact that `|1 - phi|^n / sqrt(5) < 1/2` when `n` is a nonnegative integer.
Jason
Didn't know that adjustment always have used the other form, but that is a nice optimisation
JDunkerley
+20  A: 

The nth Fibonacci number is given by

f(n) = Floor(phi^n / sqrt(5) + 1/2)

where

phi = (1 + sqrt(5)) / 2

Assuming that the primitive mathematical operations (+, -, * and /) are O(1) you can use this result to compute the nth Fibonacci number in O(log n) time (O(log n) because of the exponentiation in the formula).

In C#:

static double inverseSqrt5 = 1 / Math.Sqrt(5);
static double phi = (1 + Math.Sqrt(5)) / 2;
/* should use 
   const double inverseSqrt5 = 0.44721359549995793928183473374626
   const double phi = 1.6180339887498948482045868343656
*/

static int Fibonacci(int n) {
    return (int)Math.Floor(Math.Pow(phi, n) * inverseSqrt5 + 0.5);
}
Jason
You're killing me here Jason... See your edit history.
Matthew Scharley
@Matthew Scharley: Sorry dude.
Jason
I swear, there needs to be a warning for that... "Oh wait, this post has changed since you started editing, do you want to look over the changes?"
Matthew Scharley
As an aside, I'd probably advocate making those variables `const` rather than `static`, just to be safe.
Matthew Scharley
@Matthew Scharley: I agree you with on the need for a warning.
Jason
@Matthew Scharley: Yes, they probably should, but then I would have to key in the decimal value for `1 / Math.Sqrt(5)` and `(1 + Math.Sqrt(5)) / 2` as C# won't let `const` values be computed using `Math.Sqrt`. For our purposes here, the formula are clearer for understanding than the mysterious decimal values would be.
Jason
@Jason: that's what comments are for. But as it's static the difference is so small it's not worth messing with.
Joel Coehoorn
@Joel Coehoorn: I agree with you. I was trying to keep it simple here.
Jason
thanks Jason..i was looking for this
Biswajyoti Das
@Downvoter: Really?
Jason
@Second Downvoter: Really?
Jason
@Json I have not downvoted you, but others may be doing so because your answer suggests that the Nth fibonacci number can be computed in O(log n) time, which is false. Your code is computing an approximation.Your code would be at least O(n) in arbitrary precision, because the length of the answer is O(n).
PeterAllenWebb
@PeterAllenWebb: The formula provided is not an approximation. The nth Fibonacci number is equal to the floor of `phi^n / sqrt(5) + 1/2` where `phi = (1 + sqrt(5)) / 2`. This is a fact. Second, I understand the point that others are making about the length of the answer being `O(n)` but I have added a remark to my answer assuming that the primitive mathematical operations take constant time (I know they are not unless you bound the inputs). My point is that we can find the nth Fibonacci number in `O(log n)` arithmetic operations.
Jason
@Jason: Assuming that exponentiation is O(1) too makes the whole algorithm O(1). That would be nice, however, exponentiation is not O(1) and neither are the other primitive mathematical operations. So in short, the formula is nice, but it doesn't calculate the result in sub-linear time.
yairchu
+17  A: 

You can do it by exponentiating a matrix of integers as well. If you have the matrix

    / 1  1 \
M = |      |
    \ 1  0 /

then (M^n)[1, 2] is going to be equal to the nth Fibonacci number, if [] is a matrix subscript and ^ is matrix exponentiation. For a fixed-size matrix, exponentiation to an positive integral power can be done in O(log n) time in the same way as with real numbers.

EDIT: Of course, depending on the type of answer you want, you may be able to get away with a constant-time algorithm. Like the other formulas show, the nth Fibonacci number grows exponentially with n. Even with 64-bit unsigned integers, you'll only need a 94-entry lookup table in order to cover the entire range.

SECOND EDIT: Doing the matrix exponential with an eigendecomposition first is exactly equivalent to JDunkerly's solution below. The eigenvalues of this matrix are the (1 + sqrt(5))/2 and (1 - sqrt(5))/2.

Pillsy
Use the eigen decomposition of M to calculate M^n efficiently.
Pete Kirkham
Proposed method is fine for calculations in integers (probably with long arithmetic). Approach with eigen decomposition is not interesting: if you don't need integer calculations, then use formula from Jason's answer.
Konstantin
@Konstantin The formula from Jason's answer is the result given by eigen decomposition, so you're contradicting yourself.
Pete Kirkham
@Pete Kirkham That formula can be obtained by several methods: characteristics equation, eigen decomposition, proof by induction. I'm not sure, that eigen decomposition is the easiest one. In any case it is well-known, and it is easier to use it immediately
Konstantin
+6  A: 

One of the exercises in SICP is about this, which has the answer described here.

In the imperative style, the program would look something like

Function Fib(count)
    a ← 1
    b ← 0
    p ← 0
    q ← 1

    While count > 0 Do
        If Even(count) Then
             pp² + q²
             q ← 2pq + q²
             countcount ÷ 2
        Else
             abq + aq + ap
             bbp + aq
             countcount - 1
        End If
    End While

    Return b
End Function
Cirno de Bergerac
That has to be the best-formatted psuedocode I've ever seen on a *computer*. I'm impressed. :)
Lucas Jones
count wasn't initialized
yairchu
+1, for great formatting!
Regent
+1  A: 

Just giving a pointer to a reddit discussion about the topic. It has some nice comments.

csl
no 1 comment: "TLDR version: Bignum operations aren't O(1).". reddit is smart
yairchu
+19  A: 
Pete Kirkham
+1 - Awesome stuff, as usual. What did you use to typeset it? LaTeX?
duffymo
no, just patience and a text editor
Pete Kirkham
+5  A: 

It's impossible!

As stated above, the formula for fibonacci numbers is:

fib n = floor (phin/√5 + 1/2)

fib n ~= phin/√5

How many digits is fib n?

numDigits (fib n) = log (fib n) = log (phin/√5) = log phin - log √5 = n * log phi - log √5

numDigits (fib n) = n * const + const

it's O(n)

Since the requested result is of O(n), it can't be calculated in less than O(n) time.

If you only want the lower digits of the answer, then it is possible to calculate in sub-linear time using the matrix exponentiation method.

yairchu
This is the only fully correct answer that has been posted.
PeterAllenWebb
+1  A: 

using R

l1 <- (1+sqrt(5))/2
l2 <- (1-sqrt(5))/2

P <- matrix(c(0,1,1,0),nrow=2) #permutation matrix
S <- matrix(c(l1,1,l2,1),nrow=2)
L <- matrix(c(l1,0,0,l2),nrow=2)
C <- c(-1/(l2-l1),1/(l2-l1))

k<-20 ; (S %*% L^k %*% C)[2]
[1] 6765
gd047