【问题标题】:Memoization gone wrong or done wrong?记忆出错或做错了?
【发布时间】: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


【解决方案1】:

setdefault 参数的评估发生在调用之前,因此记忆化实际上并没有像实现 d1 那样为您节省任何时间。

如果您愿意,可以使用 functools lru_cache

@lru_cache(maxsize=None)
def d1(n):
    return 0 if n==1 else 1+d1(n//2 if n%2==0 else n*3+1)

【讨论】:

  • 非常更喜欢这种方法。 Python 已经有一个非常好的记忆装饰器。除非您有特定的理由不使用它,否则您应该使用它。此外,快速时序测试表明该解决方案比 OP 的d2 快​​约 6 倍。删除 print 调用并仅运行函数“bare”以删除字符串格式化开销使其比 d2 快约 8 倍,比 d1 快 12 倍。
猜你喜欢
  • 2017-06-16
  • 1970-01-01
  • 2014-11-09
  • 1970-01-01
  • 1970-01-01
  • 2016-05-14
  • 2019-02-01
  • 1970-01-01
  • 2011-06-25
相关资源
最近更新 更多