项目Euler#25:持续溢出错误(结果很大) - 是否与计算斐波那契数量有关?

时间:2011-07-26 12:28:36

标签: python overflow fibonacci

我正在努力解决Project Euler问题25:

  

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

我的代码片段适用于较小的数字,但是当我尝试1000位数字时,我收到错误:

OverflowError: (34, 'Result too large')

我在考虑如何计算斐波纳契数,但我尝试了几种不同的方法,但我得到了同样的错误。

这是我的代码:

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

def fibonacci(n):
    phi = (1 + pow(5, 0.5))/2           #Golden Ratio
    return int((pow(phi, n) - pow(-phi, -n))/pow(5, 0.5))   #Formula: http://bit.ly/qDumIg


n = 0
while len(str(fibonacci(n))) < 1000:
    n += 1
print n

你知道这个问题的原因是什么以及我如何改变我的代码可以避免这个问题吗?

提前致谢。

7 个答案:

答案 0 :(得分:8)

这里的问题是只有Python中的整数具有无限长度,浮点值仍然使用具有最大精度的普通IEEE类型计算。

因此,由于您使用的是近似值,使用浮点计算,最终会出现问题。

相反,尝试以正常方式计算斐波那契数列,一次计算一个数字(序列),直到达到1000位数。

即。计算1,1,2,3,5,8,13,21,34等。

以“正常方式”我的意思是:

         /  1                        , n < 3
Fib(n) = |
         \  Fib(n-2) + Fib(n-1)      , n >= 3

请注意,给出上述公式的“明显”方法对于这个特定问题是错误的,所以我会发布错误方法的代码,以确保您不会浪费时间:

def fib(n):
    if n <= 3:
        return 1
    else:
        return fib(n-2) + fib(n-1)

n = 1
while True:
    f = fib(n)
    if len(str(f)) >= 1000:
        print("#%d: %d" % (n, f))
        exit()
    n += 1

在我的机器上,上面的代码在第30个斐波那契数字附近开始真的慢,这个数字仍然只有6位数。

我修改了上面的递归方法,输出每个数字对fib函数的调用次数,这里有一些值:

#1: 1
#10: 67
#20: 8361
#30: 1028457
#40: 126491971

我可以揭示第一个1000位或更多位的Fibonacci数是序列中的第4782位数(除非我计算错误),因此递归方法中对fib函数的调用次数将是这个数字:

  

132267464567848804105889752412299767725164437081541824301708199718936580917061708039724079869466094080130656133308198562082654713166585383598879742727743646000894355282630229263781837117886954194692367517216063788207381275161763797557885925243473323252315978172073811111178946503909780208031520859709348591533219369161892604225599918513711527276938092418468224818480249182223333527940930117152695310918931362929384159708751008398694511101140231428658147857968937752179015149906626190657416186920041068465380879643268580928428682005316487919255795992233311207582682834951315813760433667482672183713587589020390424793348956115895080011387683688405958828571381050297305205​​7892127879455668391150708346800909439629659013173202984026200937561704281672042219641720514989818775239313026728787980474579564685426847905299010548673623281580547481750413205269166454195584292461766536845931986460985315260676689935535552432994592033224633385680958613360375475217820675316245314150525244440638913595353267694721 961

这只是 第4782号。实际值是所有斐波纳契数从1到4782的所有值的总和。这是不可能完成的。

事实上,如果我们给代码1年的运行时间(简化为365天),并假设机器每秒可以进行10.000.000.000次调用,算法将达到第83个数字,它仍然只有18位数。

答案 1 :(得分:3)

实际上,考虑到上面给出的避免浮点数的建议通常是Project Euler问题的好建议,在这种情况下它是不正确的。斐波那契数可以通过公式F_n = phi ^ n / sqrt(5)来计算,使得大于一千位的第一个斐波那契数可以被计算为10 ^ 999 <1。 phi ^ n / sqrt(5)。将对数取为两边的十分之一 - 回想一下,sqrt(5)与5 ^(1/2)相同 - 得到999&lt; n log_10(phi)-1 / 2log_10(5),求解n得到(999 + 1/2 log_10(5))/ log_10(phi)&lt; ñ。该等式的左侧评估为4781.85927,因此给出千位数的最小n是4782。

答案 2 :(得分:3)

您可以使用sliding window trick迭代计算Fibonacci序列的项,而不是使用闭合形式(或者按照通常定义的方式递归执行)。

用于查找fib(n)的Python版本如下:

def fib(n):
    a = 1
    b = 1
    for i in range(2, n):
        b = a + b
        a = b - a
    return b

当F(1)定义为1时,这适用于Project Euler 25。

我不会在这里给出问题的确切解决方案,但可以重新编写上面的代码,以便跟踪n,直到达到哨兵值(10**999)。

答案 3 :(得分:1)

像这样的迭代解决方案没有执行任何问题。我在不到一秒的时间内得到答案。

def fibonacci():
    current = 0 
    previous = 1
    while True:
        temp = current
        current = current + previous
        previous = temp
        yield current


def main():
for index, element in enumerate(fibonacci()):
    if len(str(element)) >= 1000:
        answer = index + 1 #starts from 0
        break
print(answer)

答案 4 :(得分:0)

import math as m
import time
start = time.time()
fib0 = 0
fib1 = 1
n = 0
k = 0
count = 1    
while k<1000 :
        n = fib0 + fib1
        k = int(m.log10(n))+1
        fib0 = fib1
        fib1 = n
        count += 1
print n 
print count
print time.time()-start

在我的电脑上需要0.005388秒。没有什么花哨只是遵循简单的代码。 迭代总是会更好。递归对我来说也很长。 还使用数学函数计算数字中的位数,而不是将数字取在列表中并迭代它。节省了大量时间

答案 5 :(得分:0)

这是我非常简单的解决方案

list = [1,1,2]
for i in range(2,5000):
    if len(str(list[i]+list[i-1])) == 1000:
        print (i + 2)
        break
    else:
        list.append(list[i]+list[i-1])

这是一种“流氓”的方式,但是如果你将1000更改为除了一个之外的任何数字,它就会正确。

答案 6 :(得分:0)

您可以使用数据类型Decimal。这有点慢,但你可以有任意精度。

所以你的代码:

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

from Decimal import *

def fibonacci(n):
    phi = (Decimal(1) + pow(Decimal(5), Decimal(0.5))) / 2           #Golden Ratio
    return int((pow(phi, Decimal(n))) - pow(-phi, Decimal(-n)))/pow(Decimal(5), Decimal(0.5)))


n = 0
while len(str(fibonacci(n))) < 1000:
    n += 1
print n