为什么在Python中,即使没有重复计算,记忆化也可以加速阶乘运算?

为什么在Python中,即使没有重复计算,记忆化也可以加速阶乘运算?,python,algorithm,memoization,Python,Algorithm,Memoization,这是我的密码 import timeit fac_mem = {} def fac(k): if k < 2: return 1 if k not in fac_mem: fac_mem[k] = k*fac(k-1) return fac_mem[k] def fact(k): if k < 2: return 1 return k*fact(k-1) if __name__ == "__main__": print timeit.timeit

这是我的密码

import timeit

fac_mem = {}

def fac(k):
  if k < 2: return 1
  if k not in fac_mem:
    fac_mem[k] = k*fac(k-1)
return fac_mem[k]

def fact(k):
  if k < 2: return 1
  return k*fact(k-1)

if __name__ == "__main__": 
  print timeit.timeit("fac(7)","from __main__ import fac")
  print timeit.timeit("fact(7)","from __main__ import fact")
  print timeit.timeit("fac(70)","from __main__ import fac")
  print timeit.timeit("fact(70)","from __main__ import fact")
那么,在程序运行之前,dict是空的,为什么记忆仍然加快了阶乘运算?我的意思是,这两个版本的调用次数是相同的。 然后我稍微修改了代码,将dict移到函数中

def fac(k):
  fac_mem = {}
  if k < 2: return 1
  if k not in fac_mem:
    fac_mem[k] = k*fac(k-1)
  return fac_mem[k]
为什么慢一点?这是因为函数内部生成的值临时存储在堆栈中吗?外部变量是全局变量,但是什么让它如此快速?
请有人帮我清理一下好吗?非常感谢。

当您运行
timeit.timeit
而不为关键字
number
指定参数时,它将执行您传递给它1000000次的代码。如果在Python的全局范围中保留一个名为
fac_mem
的全局字典,它将在该字典中保存第一次执行的结果

因此,您的记忆阶乘函数只会在第一次执行时变慢,因为它实际上必须进行计算。剩下的9999999次,它只需在字典中查找答案,避免进行计算

如果将字典保留在函数中,则每次Python离开函数中的作用域后,字典都会被销毁(因为函数返回时,对字典的所有引用都会被销毁)。因此,词典将无法保存其答案,并且在将来执行时仍将保持缓慢


当您运行
timeit.timeit
而不为关键字
number
指定参数时,它将执行您传递给它1000000次的代码。如果在Python的全局范围中保留一个名为
fac_mem
的全局字典,它将在该字典中保存第一次执行的结果

因此,您的记忆阶乘函数只会在第一次执行时变慢,因为它实际上必须进行计算。剩下的9999999次,它只需在字典中查找答案,避免进行计算

如果将字典保留在函数中,则每次Python离开函数中的作用域后,字典都会被销毁(因为函数返回时,对字典的所有引用都会被销毁)。因此,词典将无法保存其答案,并且在将来执行时仍将保持缓慢

  • 你写的回忆录永远挂在答案上。这意味着多次询问同一个答案真的很快。只有第一次调用需要计算任何东西
  • timeit对函数进行基准测试的方式是运行数百次,并对所有调用取平均值。这有助于它更准确
  • 这意味着1个正常速度呼叫和99个超快呼叫的平均值一起计算。这使得记忆功能看起来更快

    如果您想测量实际速度,您需要在每次测试调用后清除记忆字典

  • 你写的回忆录永远挂在答案上。这意味着多次询问同一个答案真的很快。只有第一次调用需要计算任何东西
  • timeit对函数进行基准测试的方式是运行数百次,并对所有调用取平均值。这有助于它更准确
  • 这意味着1个正常速度呼叫和99个超快呼叫的平均值一起计算。这使得记忆功能看起来更快


    如果你想测量实际速度,你需要在每次测试调用后清除你的记忆词典。

    更好的问题:为什么CS采用了可笑的术语记忆?@JeremyWest@christopheoca是的,我理解词源。仅仅因为有理由并不意味着这是一个好理由:)。每次我看到那个词我都会畏缩。看起来像是打字错误@JeremyWest当我几周前得知这件事的时候,一开始我还以为这是个打字错误呢!但后来我终于习惯了,现在我很喜欢这个词更好的问题:为什么CS采用了可笑的术语Memorization?@JeremyWest@Christopherica是的,我理解词源。仅仅因为有理由并不意味着这是一个好理由:)。每次我看到那个词我都会畏缩。看起来像是打字错误@JeremyWest当我几周前得知这件事的时候,一开始我还以为这是个打字错误呢!但后来我终于习惯了,现在我很喜欢这个词谢谢,但是我是否已经通过将dict移动到阶乘函数中获得了实际速度?不,将字典移动到函数中会使其无效,因为它是局部的,并且总是空的。谢谢,但是我是否已经通过将dict移动到阶乘函数中获得了实际速度?不,将字典移动到函数内部会使它无用,因为它是本地的,并且总是空的。
    def fac(k):
      fac_mem = {}
      if k < 2: return 1
      if k not in fac_mem:
        fac_mem[k] = k*fac(k-1)
      return fac_mem[k]
    
    1.92900857225
    0.910026658388
    25.5475004875
    14.2164999769