我是新手编码并尝试进行项目euler练习以提高我的编码知识。关于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)
答案 0 :(得分:2)
要计算fib(10)
,请执行以下操作:
fib(10) = fib(9) + fib(8)
其中递归计算fib(9)
:
fib(9) = fib(8) + fib(7)
看到问题? fib(8)
的结果必须计算两次!为了进一步扩展表达式(例如,得到fib(8)
的结果),当数字很大时,冗余计算是巨大的。
递归本身不是问题,但你必须存储较小的斐波纳契数的结果,而不是计算相同的表达式。一种可能的解决方案是使用字典来存储中间结果。
答案 1 :(得分:0)
您正在使用对其他解决方案使用普通迭代循环的函数的递归调用。
进行函数调用会受到一些调用和返回的开销。对于更大数量的n,你将有很多这些函数调用。
一遍又一遍地追加到列表并将其总结可能也比通过累加器这样做慢。
答案 2 :(得分:0)
每次进入while循环时,您的解决方案都会调用递归函数(带2次递归)。然后在循环中再次运行相同的功能。 另一种解决方案只是添加数字,然后进行排列。 我猜你并不真的需要斐波纳契,但如果你坚持使用它,只运行一次并保存结果,而不是重新运行它。 另外,您存储了所有结果并在最后汇总。这也消耗了一些时间(不仅如此),也许你不需要存储中间结果。
答案 3 :(得分:0)
正如其他几个答案所指出的那样,递归会导致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次拨打{{1} }!
但稍后,当您致电fib()
时,还会调用fib(9)
,这需要重新开始所有工作(另外67次调用fib(8)
)。这很快就会失控。如果fib()
能够记住已经计算fib()
并记住结果,那会更好。这称为缓存或 memoization 。
幸运的是,Python的标准库只有一个装饰器,functools.lru_cache:
fib(8)
在我的计算机上,您的程序执行时间为27 56秒内的{56}的5 56 {5}次调用,以及0.028秒内的35次调用。