【问题标题】:Project Euler #2 Python 3.5 Help on LatencyProject Euler #2 Python 3.5 关于延迟的帮助
【发布时间】:2016-08-12 15:59:19
【问题描述】:

我是编码新手,并尝试进行项目欧拉练习以提高我的编码知识。关于 Project Euler #2,我遇到了几种解决方案。

但是,与我找到的解决方案相比,我想知道为什么我的代码需要更长的时间来计算。

如果有人能指导我了解两者之间的区别,我将不胜感激。


我的代码:

def fib(n):
if n==0:
    return 0
elif n == 1:
    return 1
else:
    f=fib(n-1)+fib(n-2)
    return f

i=0
store=[]
while fib(i)<=4000000:
    i += 1
    if fib(i)%2 == 0:
        store.append(fib(i))

print('The total is: '+str(sum(store)))

我找到的在线解决方案:

a = 1
b = 2
s = 0
while b <= 4000000:
if not b % 2:
    s += b
a, b = b, a + b
print(s)

【问题讨论】:

  • 与任何答案无关,但您可以通过从偶数斐波那契跳到偶数斐波那契来进一步改进迭代解决方案,这样您就可以摆脱 if 语句。每个第 3 个斐波那契数都是偶数,因此连续应用该规则 3 次:while b &lt; 4000000: s += b; a, b = a+2*b, 2*a+3*b
  • 您发布的程序无效;请修正你的缩进。

标签: python python-3.x fibonacci


【解决方案1】:

正如其他几个答案所指出的,递归会导致您的 fib() 函数被非常频繁地调用,实际上是 111 561 532 次。通过添加计数器很容易看到这一点:

count = 0

def fib(n):
    global count
    count += 1

    if n==0:

# the rest of your program

print(count)

有两种方法可以解决此问题;将您的程序重写为迭代而不是递归(就像您发布的其他解决方案一样),或者缓存来自fib() 的中间结果。

看,你打电话给fib(8),而后者又必须调用fib(7)fib(6)等等。仅计算fib(8)就需要67次调用fib()

但稍后,当您调用fib(9) 时, 调用fib(8),它必须重新完成所有工作(67 次调用fib())。这很快就会失控。如果fib() 能记住它已经计算过fib(8) 并记住结果,那就更好了。这称为 缓存memoization

幸运的是,Python 的标准库有一个专门用于此目的的装饰器,functools.lru_cache

from functools import lru_cache

@lru_cache()
def fib(n):
    if n==0:

...

在我的计算机上,您的程序执行从 27 秒内对 fib() 的 111 561 532 次调用到 0.028 秒内的 35 次调用。

【讨论】:

    【解决方案2】:

    您的解决方案每次进入您的 while 循环时都会调用一个递归函数(带有 2 个递归)。然后在循环中再次运行相同的函数。 另一种解决方案只添加数字,然后进行排列。 我猜你并不真的需要斐波那契,但如果你坚持使用它,只运行一次并保存结果,而不是重新运行它。 另外,您可以存储所有结果并在最后求和。这也会消耗一些时间(不仅如此),也许您不需要存储中间结果。

    【讨论】:

      【解决方案3】:

      您正在对另一个解决方案使用普通迭代循环的函数进行递归调用。

      进行函数调用必然会带来一些调用和返回的开销。对于更大数量的 n,您将有很多这样的函数调用。

      一遍又一遍地附加到一个列表并总结它可能也比通过累加器执行此操作要慢。

      【讨论】:

      • 虽然由于调用函数的开销,迭代方法比递归方法更好,正如余浩指出的那样,递归并不是主要问题。您可以使其几乎与使用记忆的迭代解决方案一样快。但是没有它,你最终会计算 fib(x) 百万/十亿次的价值。
      【解决方案4】:

      用你的实现来计算fib(10)

      fib(10) = fib(9) + fib(8)
      

      其中fib(9)是递归计算的:

      fib(9) = fib(8) + fib(7)
      

      看到问题了吗? fib(8) 的结果要计算两次!进一步扩展表达式(例如,得到fib(8)的结果),数量大时冗余计算量大。


      递归本身不是问题,但是您必须存储较小斐波那契数的结果,而不是不断地计算相同的表达式。一种可能的解决方案是使用字典来存储中间结果。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2010-09-17
        • 1970-01-01
        • 2015-01-23
        • 1970-01-01
        • 1970-01-01
        • 2011-03-23
        • 1970-01-01
        • 2011-02-08
        相关资源
        最近更新 更多