7

Project Euler problem 25:

The Fibonacci sequence is defined by the recurrence relation:

Fn = Fn−1 + Fn−2, where F1 = 1 and F2 = 1. Hence the first 12 terms will be F1 = 1, F2 = 1, F3 = 2, F4 = 3, F5 = 5, F6 = 8, F7 = 13, F8 = 21, F9 = 34, F10 = 55, F11 = 89, F12 = 144

The 12th term, F12, is the first term to contain three digits.

What is the first term in the Fibonacci sequence to contain 1000 digits?

I made a brute force solution in Python, but it takes absolutely forever to calculate the actual solution. Can anyone suggest a non brute force solution?

def Fibonacci(NthTerm):
    if NthTerm == 1 or NthTerm == 2:
        return 1 # Challenge defines 1st and 2nd term as == 1
    else:  # recursive definition of Fib term
        return Fibonacci(NthTerm-1) + Fibonacci(NthTerm-2)

FirstTerm = 0 # For scope to include Term in scope of print on line 13
for Term in range(1, 1000): # Arbitrary range
    FibValue = str(Fibonacci(Term)) # Convert integer to string for len()
    if len(FibValue) == 1000:
        FirstTerm = Term
        break # Stop there
    else:
        continue # Go to next number
print "The first term in the\nFibonacci sequence to\ncontain 1000 digits\nis the", FirstTerm, "term."
Nayuki
  • 17,911
  • 6
  • 53
  • 80
slinky773
  • 143
  • 1
  • 8
  • a brute-force solution does not need to take for ever. it is your recursion that is way too slow. just google for a better fibonacci algorithm – gefei Dec 27 '13 at 22:42
  • 3
    Your bruteforce complexity is horrible, you got O(2^n). Try to use three arguments: the number of terms to create, and the two previous results. That what you'll get linear time, and it should be fast enough. – toasted_flakes Dec 27 '13 at 22:43

8 Answers8

12

You can write a fibonacci function that runs in linear time and with constant memory footprint, you don't need a list to keep them. Here's a recursive version (however, if n is big enough, it will just stackoverflow)

def fib(a, b, n):
    if n == 1:
        return a
    else: 
        return fib(a+b, a, n-1)


print fib(1, 0, 10) # prints 55

This function calls itself only once (resulting in around N calls for a parameter N), in contrast with your solution which calls itself twice (around 2^N calls for a parameter N).

Here's a version that won't ever stackoverflow and uses a loop instead of recursion:

def fib(n):
    a = 1
    b = 0
    while n > 1:
        a, b = a+b, a
        n = n - 1
    return a

print fib(100000)

And that's fast enough:

$ time python fibo.py 
3364476487643178326662161200510754331030214846068006390656476...

real    0m0.869s

But calling fib until you get a result big enough isn't perfect: the first numbers of the serie are calculated multiple times. You can calculate the next fibonacci number and check its size in the same loop:

a = 1
b = 0
n = 1
while len(str(a)) != 1000:
    a, b = a+b, a
    n = n + 1
print "%d has 1000 digits, n = %d" % (a, n)
Community
  • 1
  • 1
toasted_flakes
  • 2,502
  • 1
  • 23
  • 38
2

Why hasn't anybody used generators for this? This is a brute force solution, but it is very quick:

def fibo():
    a = 0
    b = 1
    while True:
        yield b
        a,b = b,a+b

This gives a generator that computes the Fibonacci sequence. For example

f = fibo()
[next(f) for i in range(10)]

produces

[1,1,2,3,5,8,13,21,34,55]

Using this, we can solve the problem like so:

f = enumerate(fibo())
x = 0
while len(str(x)) < 1000:
    i,x = next(f)

print("The %d-th term has %d digits"%(i+1,len(str(x))))

This produces the output

The 4782-th term has 1000 digits

The generator computes the sequence and produces terms 1 by 1 and this solution runs nearly instantly.

Matthew
  • 7,440
  • 1
  • 24
  • 49
1

Two things can be optimized a lot with one little change in your code. These two things are:

  • You compute each fibonacci number using two other fibonacci numbers, resulting in an exponential complexity (which explodes even if you'd only compute a single but high fibonacci number).

  • You don't remember any previous computed fibonacci number to compute the next in your loop.

Simply remember all computed fibonacci numbers as a private implementation detail in Fibonacci and you get rid of both performance problems. You can do this by using a simple dynamic array to which you add the result if it wasn't cached before.

Pseudo-code (I don't speak Python but this can be easily implemented):

def Fibonacci(NthTerm):
    if (cache contains NthTerm)
        return cache[NthTerm]
    else
        FibValue = Fibonacci(NthTerm-1) + Fibonacci(NthTerm-2)
        cache[NthTerm] = FibValue
        return FibValue

This will result in very restricted recurion, since you compute the N-th fibonacci number only if you already know (and cached) the (N-1)-th number.

This optimization works even if you need any fibonacci number (for future problems), but in this particular case we know that we only need to remember the last two numbers since we're never going to ask for old numbers again. So you don't need a whole list of numbers but only two, which you "cycle through" for each step in your main loop. Something like

f1, f2 = f2, f1 + f2

within the loop and an initialization like

f1, f2 = 1, 1

will essentially replace your function Fibonacci and its performance problems, but it restricts you to this limited use case.

leemes
  • 44,967
  • 21
  • 135
  • 183
1

You can try to use newton's approximation method on binet's formula. The idea is to find a tangent line on the graph and use the x-intercept of that line to approximate the value of the zero of the graph.

Kevin
  • 61
  • 5
  • 2
    This is really a comment, not an answer. With a bit more rep, [you will be able to post comments](//stackoverflow.com/privileges/comment). – Raju Jul 18 '16 at 01:02
0

Instead of recursively calculating every term each time, make an array of terms, then you can calculate a term by adding terms[-1] and terms[-2]

Paul Becotte
  • 9,767
  • 3
  • 34
  • 42
  • Not the best option, he don't need to keep all the fibonnacci numbers but just the last two – toasted_flakes Dec 27 '13 at 22:44
  • Good point! I am not super familiar with how things work under the hood though... it seems like it would take a couple more operations to get rid of the old variables. Would this be sacrificing speed for memory? – Paul Becotte Dec 27 '13 at 22:47
  • You can just keep them in 2 variables that you update at each iteration, the speed doesn't change, and memory footprint keeps constant – toasted_flakes Dec 27 '13 at 22:51
0

Here is the Java version in constant space and linear time:

    static int q24(){
    int index = 3;
    BigInteger fn_2 = new BigInteger("1");
    BigInteger fn_1 = new BigInteger("1");
    BigInteger fn   = fn_1.add(fn_2);
    while(fn.toString().length()<1000){
        fn_2 = fn_1;
        fn_1 = fn;
        fn = fn_2.add(fn_1);
        index++;
    }       
    return index;
}
0

you can use Memorization :

m={}

def fub(n):
     if n not in m:
        if n <= 2 :
           m[n] =  1
        else:
           m[n] =  fub(n-1) + fub(n-2)

     return m[n]

i=1
while len(str(fub(i))) != 1000:
   i+=1
print(i)
SoufianeK
  • 1
  • 3
0

If you are using Kotlin, a very simple solution.

import java.math.BigInteger
val bound: BigInteger = BigInteger.TEN.pow(999)
var index = 2
var fib = BigInteger.ONE
var prevFib = BigInteger.ONE
while (fib < bound) {
    prevFib = fib.also { fib += prevFib }
    index++
}
println(index)
Samuel Dervis
  • 366
  • 1
  • 9