Euler项目的非强力解决方案25

时间:2013-12-27 22:38:00

标签: python fibonacci brute-force

Project Euler problem 25

  

Fibonacci序列由递归关系定义:

     

F n = F n-1 + F n-2 ,其中F 1 = 1 和F 2 = 1.因此前12个术语   将是F 1 = 1,F 2 = 1,F 3 = 2,F 4 = 3, F 5 = 5,F 6 = 8,F 7 = 13,F 8 =   21,F 9 = 34,F 10 = 55,F 11 = 89,F 12 = 144 < / p>      

第12个学期,F 12 ,是第一个包含三位数的术语。

     

Fibonacci序列中第一个包含1000的术语是什么   数字?

我在Python中制作了一个强力解决方案,但计算实际解决方案绝对是永远的。任何人都可以提出非暴力解决方案吗?

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."

9 个答案:

答案 0 :(得分:12)

你可以编写一个在线性时间内运行且具有恒定内存占用的斐波那契函数,你不需要一个列表来保存它们。 这是一个递归版本(但是,如果n足够大,它只会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

这个函数只调用一次(导致大约N次调用参数N),与你自己调用两次的解决方案相比(大约2 ^ N次调用参数N)。

这是一个不会堆栈溢出并使用循环而不是递归的版本:

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

print fib(100000)

这足够快:

$ time python fibo.py 
3364476487643178326662161200510754331030214846068006390656476...

real    0m0.869s

但是在你得到足够大的结果之前调用fib并不完美:系列的第一个数字会多次计算。 您可以计算下一个斐波纳契数,并在同一循环中检查其大小:

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)

答案 1 :(得分:4)

使用binet's formula。这是找到斐波纳契数的最快方法,它不使用递归。

答案 2 :(得分:1)

只需对代码进行一点改动,就可以优化两件事。这两件事是:

  • 您使用另外两个斐波纳契数来计算每个斐波纳契数,导致指数复杂性(即使您只计算单个但高斐波那契数,也会爆炸)。

  • 您不记得先前计算过的斐波纳契数,以计算循环中的下一个。

简单地记住所有计算的斐波纳契数字作为Fibonacci中的私有实现细节,您可以摆脱两个性能问题。您可以通过使用一个简单的动态数组来完成此操作,如果之前未缓存该结果,则添加结果。

伪代码(我不会说Python,但这很容易实现):

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

这将导致非常有限的重复,因为只有在您已经知道(和缓存)第(N-1)个数字时才计算第N个斐波纳契数。

即使你需要任何斐波那契数字(对于未来的问题),这种优化仍然有效,但在这种特殊情况下,我们知道我们只需要记住最后两个数字,因为我们永远不会再次询问旧号码。因此,您不需要一个完整的数字列表,只需要两个,您可以在主循环中的每个步骤中“循环”。像

这样的东西
f1, f2 = f2, f1 + f2
循环内的

之类的初始化
f1, f2 = 1, 1

基本上会替换你的函数Fibonacci及其性能问题,但它会限制你使用这个有限的用例。

答案 3 :(得分:1)

您可以尝试在newton's approximation method上使用binet's formula。我们的想法是在图表上找到一条切线,并使用该线的x轴截距来近似图形零点的值。

答案 4 :(得分:1)

为什么没有人为此使用过发电机?这是一个强力解决方案,但它很快:

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

这给出了一个计算Fibonacci序列的生成器。例如

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

产生

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

使用这个,我们可以像这样解决问题:

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))))

这会产生输出

The 4782-th term has 1000 digits

生成器计算序列并逐个生成术语,此解决方案几乎立即运行。

答案 5 :(得分:0)

不是每次都递归计算每个术语,而是制作一个术语数组,然后你可以通过添加术语[-1]和术语[-2]来计算术语

答案 6 :(得分:0)

以下是常量空间和线性时间的Java版本:

    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;
}

答案 7 :(得分:0)

您可以使用记忆:

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)

答案 8 :(得分:0)

如果您使用 Kotlin,这是一个非常简单的解决方案。

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)