【发布时间】:2021-08-11 17:48:35
【问题描述】:
我正在学习一点 Python,并希望通过字典记忆来加速函数。但是越来越慢了!这是因为 Python 中的字典速度很慢,还是因为我应该做的不是使用 setdefault ?如果是这样,那么在 Python 中推荐和/或最快的记忆方式是什么?
D={}
d1=lambda n: D.setdefault( n, 0 if n==1 else 1+d1(n//2 if n%2==0 else n*3+1) )
d2=lambda n: 0 if n==1 else 1+d2(n//2 if n%2==0 else n*3+1)
for n in range(1,40001): print("d(",n,") =",d2(n))
#print(D) #to see that D is actually filled
d1 和 d2 之间的唯一区别是 d1 使用记忆,对于相同的输入,它们都返回相同的结果。 Perl 中同样的事情导致了巨大的加速,运行时:
Memoization Python Perl
----------- ------ -----
Yes 1.14s 0.19s
No 0.81s 1.33s
这四个测试作为一个单行:
time python -c'D={};d=lambda n:D.setdefault(n, 0 if n==1 else 1+d(n//2 if n%2==0 else n*3+1));[print("d(",n,")=",d(n)) for n in range(1,40001)]'|tail
time python -c' d=lambda n: 0 if n==1 else 1+d(n//2 if n%2==0 else n*3+1);[print("d(",n,")=",d(n)) for n in range(1,40001)]'|tail
time perl -E'sub d{my$n=pop;$d{$n}//=do{$n==1?0:1+d($n%2?$n*3+1:$n/2)}}say"d($_)=".d($_) for 1..40000'|tail
time perl -E'sub d{my$n=pop; $n==1?0:1+d($n%2?$n*3+1:$n/2)}say"d($_)=".d($_) for 1..40000'|tail
(d1 和 d2 返回collatz series 中的停止距离,其中下一个数字在偶数时为 n/2,在奇数时为 3*n+1。示例:d(24) = 10 因为我们需要十步才能到达 1从 24 开始:12、6、3、10、5、16、8、4、2、1,即十步)
编辑:
在阅读了有用的 cmets 和答案之后,我现在发现出了问题,并且有了这个新的更快 (0.13s) 函数 d3,这将是我对真实代码的处理方法。或者@lru_cache,如果我能让它工作的话。在某些情况下限制内存使用似乎是个好主意。
def d3(n):
D[n] = D[n] if n in D else 0 if n==1 else 1+d3(n//2 if n%2==0 else n*3+1)
return D[n]
【问题讨论】:
-
dict.setdefault()的第二个参数(就像 Python 中的 every 参数一样)在调用该方法之前被完全评估。因此,您仍在进行完全递归,只是忽略了您已记住上一次调用结果的情况的结果。 -
要添加到@jasonharper 所说的内容,您需要明确检查结果是否已经在字典中,如果没有则仅添加。
标签: python memoization