我在euler项目上尝试了一个问题,我需要找到所有斐波那契项的总和,小于400万。这花了我很长时间,但后来我发现我可以用回忆录来做,但似乎还需要很长时间。经过大量研究,我发现我可以使用一个名为lru_cache的内置模块。我的问题是:为什么它没有回忆录那么快
这是我的密码:
from functools import lru_cache
@lru_cache(maxsize=1000000)
def fibonacci_memo(input_value):
global value
fibonacci_cache = {}
if input_value in fibonacci_cache:
return fibonacci_cache[input_value]
if input_value == 0:
value = 1
elif input_value == 1:
value = 1
elif input_value > 1:
value = fibonacci_memo(input_value - 1) + fibonacci_memo(input_value - 2)
fibonacci_cache[input_value] = value
return value
def sumOfFib():
SUM = 0
for n in range(500):
if fibonacci_memo(n) < 4000000:
if fibonacci_memo(n) % 2 == 0:
SUM += fibonacci_memo(n)
return SUM
print(sumOfFib())
顺便说一下,代码是有效的。当我使用lru_缓存模块时,运行它不到一秒钟
为了完整起见,这里是@NotDijkstra的答案中描述的一般思想的实现,加上我拙劣的优化,包括用整数算法实现的“封闭形式”解决方案
我们可以看到,“智能”方法不仅比简单乘法快一个数量级,而且与Python大整数比简单乘法使用得更好这一事实(感谢@NotDijkstra)的伸缩性更好
另一个答案确实是计算斐波那契序列的正确方法,但你也应该知道为什么你的记忆不起作用。具体而言:
fibonacci_cache = {}
这一行位于函数内部意味着每次调用fibonacci_memo时都会清空缓存
你不应该计算斐波那契序列,甚至不应该用动态规划。由于斐波那契序列满足常系数、常阶的线性递推关系,因此它们的和序列也将满足线性递推关系
绝对不要缓存所有的值。这会给你不必要的内存消耗。当重复出现的顺序不变时,您只需记住重复出现顺序中的前几项即可
此外,还有一种方法可以将常阶循环转化为系统一阶循环。后者的解由矩阵的幂给出。这为
n
的大值提供了更快的算法。不过,每一步都会更加昂贵。因此,最好的方法将两者结合使用,第一种方法用于n
的小值,第二种方法用于大输入O(n)对求和使用递归
表示
S_n=F_0+F_1+...+F_n
第一个斐波那契数的和F_0,F_1,...,F_n
注意
S_{n+1}-S_n=F_{n+1}
S_{n+2}-S_{n+1}=F_{n+2}
S_{n+3}-S_{n+2}=F_{n+3}
因为
F_{n+3}=F_{n+2}+F_{n+1}
我们得到了S_{n+3}-S_{n+2}=S_{n+2}-S_n
。所以S_{n+3}=2S_{n+2}-S_n
初始条件为
S_0=F_0=1
、S_1=F_0+F_1=1+1=2
和S_2=S_1+F_2=2+2=4
您可以做的一件事是自下而上计算
S_n
,在每一步只记住前三项的值。您不需要记住S_k
的所有值,从k=0
到k=n
。这将为您提供一个具有O(1)
内存量的O(n)
算法O(ln(n))通过矩阵求幂
您还可以通过以下方式获得
O(ln(n))
算法:调用
X_n
作为包含组件S_{n+2},S_{n+1},S_{n}
的列向量所以,上面的递推式给出了递推式
X_{n+1}=AX_n
其中
A
是矩阵因此,}。要乘以
X_n=A^nX_0
。我们有{A^n
,我们可以做exponentiation by squaring相关问题 更多 >
编程相关推荐