为什么memoization可以加速Python中的阶乘,即使没有重复计算?

时间:2013-09-22 02:39:23

标签: 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("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")

这是输出

0.236774399224
0.897065011572
0.217623144304
14.1952226578

好吧,在程序运行之前,dict是空的,为什么memoization仍然加速了阶乘?我的意思是,这两个版本的调用次数是相同的。 然后我稍微更改了代码,将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

为什么慢?那是因为函数内部生成的值暂时存储在堆栈中吗?变量外部是全球性的,但是什么使它变得如此之快? 请有人帮我清理一下吗?我很感激。

2 个答案:

答案 0 :(得分:4)

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

因此,您的memoized factorial函数在第一次执行时只会很慢,而实际上它必须进行计算。剩下的999999次它只会在字典中查找答案并避免进行计算。

如果你将字典保留在函数内部,那么每次Python将函数放在函数内时它都会被销毁(因为当函数返回时,对字典的所有引用都将被销毁)。因此,字典将无法保存其答案,并且对于将来的执行仍然会保持缓慢。

Check out the documentation for timeit.

答案 1 :(得分:3)

  1. 你写的便笺永久地挂在答案上。这意味着多次要求相同的答案非常快。只有第一次通话需要计算任何东西。
  2. timeit对函数进行基准测试的方法是运行数百次并取平均所有调用。这有助于提高准确性
  3. 这意味着1个正常速度呼叫和99个超快速呼叫被平均在一起。这使得memoized函数看起来更快。

    如果要测量实际速度,则需要在每次测试调用后清除memoization字典。