32

So, we see a lot of fibonacci questions. I, personally, hate them. A lot. More than a lot. I thought it'd be neat if maybe we could make it impossible for anyone to ever use it as an interview question again. Let's see how close to O(1) we can get fibonacci.

Here's my kick off, pretty much crib'd from Wikipedia, with of course plenty of headroom. Importantly, this solution will detonate for any particularly large fib, and it contains a relatively naive use of the power function, which places it at O(log(n)) at worst, if your libraries aren't good. I suspect we can get rid of the power function, or at least specialize it. Anyone up for helping? Is there a true O(1) solution, other than the finite* solution of using a look-up table?

http://ideone.com/FDt3P

#include <iostream>
#include <math.h>
using namespace std; // would never normally do this.
     
int main() {
    int target = 10;
    cin >> target;
    // should be close enough for anything that won't make us explode anyway.
    float mangle = 2.23607610; 
     
    float manglemore = mangle;
    ++manglemore; manglemore = manglemore / 2;
    manglemore = pow(manglemore, target);
    manglemore = manglemore/mangle;
    manglemore += .5;
    cout << floor(manglemore);
}

*I know, I know, it's enough for any of the zero practical uses fibonacci has.

Michael M.
  • 10,486
  • 9
  • 18
  • 34
Jake Kurzer
  • 1,532
  • 4
  • 16
  • 25
  • 1
    there is O(1) solution: http://en.wikipedia.org/wiki/Fibonacci_number#Closed-form_expression – amit May 17 '11 at 21:40
  • 8
    That relies on the power function, which is not O(c). My example is actually that algorithm. Which is mentioned. In my question. – Jake Kurzer May 17 '11 at 21:41
  • 2
    Seems like the biggest problem is the pow function as it is imprecise. perhaps one could split it in such a way that any error would be less than 1/2 and then round? then repeat? (using the one line of math way to get the nth fibonacci) – soandos May 17 '11 at 21:42
  • 1
    What is the question, exactly? – Robert Harvey May 17 '11 at 21:42
  • Added a bit more clarity, does that help, Robert? – Jake Kurzer May 17 '11 at 21:43
  • So the question is, "Can a Fibonacci function be written to execute in O(c) time?" – Robert Harvey May 17 '11 at 21:44
  • Or faster than log^2(n), at least. – Jake Kurzer May 17 '11 at 21:45
  • 2
    Sure - just use a lookup table - there's aren't *that* many Fibonacci numbers between 1 and `FLT_MAX`. ;-) – Paul R May 17 '11 at 21:46
  • 1
    @Paul: Yeah, that's what I was thinking. ;) I think you can assume the OP wants to calculate it. – Robert Harvey May 17 '11 at 21:46
  • @Paul: I've thought about the look-up table, and I'll go ahead and mention it. It's probably worth pursuing a solution that actually calculates the answer. – Jake Kurzer May 17 '11 at 21:48
  • That's much cleaner, Robert. Thank you! – Jake Kurzer May 17 '11 at 21:51
  • What is O(c)? I have assumed big O notation, but don't know and can not find in any search what order c is. PS I can do it in O(n) but would assume (correctly) that the maths bofs can do better. – ctrl-alt-delor May 17 '11 at 22:23
  • 2
    I'll make it O(1)... O(c) is constant time, used to indicate that it may not be a single op. Doesn't look to be standard though, so... – Jake Kurzer May 17 '11 at 22:28
  • 3
    If you want a simple check, the last digits of the fibbonacci sequence form a pattern (base 16 repeats every 24, base 32 repeats every 48, base 64 repeats every 96 etc) you can use that to do a more accurate rounding. – soandos May 17 '11 at 22:42
  • @soandos Oh, that's rather good! I'll have to give that a bit of thought. – Jake Kurzer May 17 '11 at 22:46
  • @jake: if someone could find out the error on the math.pow(n), all you have to do is make sure it is less than 64 or 128 or however large you want your look up table to be, and then use that power. – soandos May 17 '11 at 22:50
  • @Jake - `O(log^2(n))`? But `pow(a,n)` can be done in `O(log n)`? – Ishtar May 17 '11 at 22:54
  • 1
    [Here's an implementation](http://stackoverflow.com/questions/6037719/fibonaccis-closed-form-expression-in-haskell/6037936#6037936) of the same formula that avoids integer arithmetic. So it's still not constant, but at least it's accurate :) – hammar May 18 '11 at 00:47
  • s/integer/floating point/ D'oh. – hammar May 18 '11 at 01:12

7 Answers7

65

Here is a near O(1) solution for a Fibonacci sequence term. Admittedly, O(log n) depending on the system Math.pow() implementation, but it is Fibonacci w/o a visible loop, if your interviewer is looking for that. The ceil() was due to rounding precision on larger values returning .9 repeating.

enter image description here

Example in JS:

function fib (n) {
  var A=(1+Math.sqrt(5))/2,
      B=(1-Math.sqrt(5))/2,
      fib = (Math.pow(A,n) - Math.pow(B,n)) / Math.sqrt(5);
      return Math.ceil(fib);
}
Joseph Lust
  • 19,340
  • 7
  • 85
  • 83
  • 2
    Nice. Works for n <= 1474. Above that result becomes infinity. – user31389 Dec 18 '15 at 11:11
  • Thanks for this; I was looking for code for this algorithm to add to a demonstration for a course. However, we use Java (1.8) and a direct translation of this made sample values such as 10, 20, 40, 45, 50 come out 1 too large. Removing that .ceil() call fixed it. – David Brown Mar 20 '16 at 00:11
  • Can anyone shed some light as to why you'd need to use the floor function in other languages? I looked through the proof and nothing was obvious as to being the reason. – PSub Dec 09 '17 at 03:11
  • This should be the best answer – paullb Jan 17 '19 at 00:57
  • Actually you don't have to compute B^n and thus make the procedure a little faster. B^n is always less than 0.5, so you can rewrite the formula as `Fib(n) = round(A^n/sqrt(5))`. https://en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding – Adam Štafa Sep 02 '20 at 16:10
  • In java both in-built Math.pow() and Math.sqrt() are O(1) so the time complexity of Binet's formula is O(1) – Bu Saeed Apr 14 '21 at 18:41
22

Given arbitrary large inputs, simply reading in n takes O(log n), so in that sense no constant time algorithm is possible. So, use the closed form solution, or precompute the values you care about, to get reasonable performance.

Edit: In comments it was pointed out that it is actually worse, because fibonacci is O(phi^n) printing the result of Fibonacci is O(log (phi^n)) which is O(n)!

Philip JF
  • 28,199
  • 5
  • 70
  • 77
  • It'll do for now, I guess. Feels a bit limp to just fob it off though. – Jake Kurzer May 19 '11 at 05:04
  • 10
    Actually it is even worse than this... True, reading the input is O(log n). But _printing_ (or even _storing_) the output is O(n), no matter what number base you use. (The output value is O(phi^n), which requires O(n) digits to represent.) – Nemo Aug 02 '11 at 15:17
18

The following answer executes in O(1), though I am not sure whether it is qualified for you question. It is called Template Meta-Programming.

#include <iostream>
using namespace std;

template <int N>
class Fibonacci
{
public:
    enum {
        value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value
    };
};

template <>
class Fibonacci<0>
{
public:
    enum {
        value = 0
    };
};

template <>
class Fibonacci<1>
{
public:
    enum {
        value = 1
    };
};

int main()
{
    cout << Fibonacci<50>::value << endl;
    return 0;
}
Dante May Code
  • 11,177
  • 9
  • 49
  • 81
8

In Programming: The Derivation of Algorithms, Anne Kaldewaij expands out the linear algebra solution to get (translated and refactored from the programming language used in that book):

template <typename Int_t> Int_t fib(Int_t n)
{
    Int_t a = 0, b = 1, x = 0, y 1, t0, t1;
    while (n != 0) {
        switch(n % 2) {
            case 1:
                t0 = a * x + b * y;
                t1 = b * x + a * y + b * y;
                x = t0;
                y = t1;
                --n;
                continue;
            default:
                t0 = a * a + b * b;
                t1 = 2 * a * b + b * b;
                a = t0;
                b = t1;
                n /= 2;
                continue;
        }
    }
    return x;
}

This has O(log n) complexity. That's not constant, of course, but I think it's worth adding to the discussion, especially given that it only uses relatively fast integer operations and has no possibility of rounding error.

Max Lybbert
  • 19,717
  • 4
  • 46
  • 69
1

Yes. Precalculate the values, and store in an array, then use N to do a lookup.

EvilTeach
  • 28,120
  • 21
  • 85
  • 141
1

Pick some largest value to handle. For any larger value, raise an error. For any smaller value than that, just store the answer at that smaller value, and keep running the calculation for the "largest" value, and return the stored value.

After all, O(1) specifically means "constant", not "fast". With this method, all calculations will take the same amount of time.

evil otto
  • 10,348
  • 25
  • 38
0

Fibonacci in O(1) space and time (Python implementation):

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

def fib(n: int):
  return int(PHI ** n / sqrt(5) + 0.5)
CCBet
  • 416
  • 1
  • 6
  • 19
  • 1
    Can you try out for which arguments this takes 5 seconds, 10, 15, 20, 30, 50, 70, 100? Does that look linear? Are the results exact? – greybeard Jul 24 '19 at 20:47