时间为什么在所有记忆化运行中几乎保持恒定?

huangapple go评论128阅读模式
英文:

Why does timeit results in almost constant time for all memoization runs?

问题

我们计算斐波那契数列:

  1. def fibo_memo(i, memo={}):
  2. if i <= 0:
  3. return 0
  4. elif i == 1:
  5. return 1
  6. elif i in memo:
  7. return memo[i]
  8. else:
  9. memo[i] = fibo_memo(i-2, memo) + fibo_memo(i-1, memo)
  10. return memo[i]
  11. def fibo_dp(i):
  12. if i <= 0:
  13. return 0
  14. elif i == 1:
  15. return 1
  16. dp = [0] * (i + 1)
  17. dp[1] = 1
  18. for j in range(2, i + 1):
  19. dp[j] = dp[j-1] + dp[j-2]
  20. return dp[i]
  21. assert(fibo_memo(100) == fibo_dp(100))

现在计时:

  1. i = 10
  2. %timeit fibo_memo(i) # 73 ns
  3. %timeit fibo_dp(i) # 309 ns
  4. i = 100
  5. %timeit fibo_memo(i) # 73 ns
  6. %timeit fibo_dp(i) # 2.54 微秒
  7. i = 1000
  8. %timeit fibo_memo(i) # 73 ns
  9. %timeit fibo_dp(i) # 33 微秒

为什么记忆化方法的执行时间几乎是常数时间,而动态规划不是?

英文:

We compute the Fibonacci number:

  1. def fibo_memo(i, memo={}):
  2. if i &lt;= 0:
  3. return 0
  4. elif i == 1:
  5. return 1
  6. elif i in memo:
  7. return memo[i]
  8. else:
  9. memo[i] = fibo_memo(i-2, memo) + fibo_memo(i-1, memo)
  10. return memo[i]
  11. def fibo_dp(i):
  12. if i &lt;= 0:
  13. return 0
  14. elif i == 1:
  15. return 1
  16. dp = [0] * (i + 1)
  17. dp[1] = 1
  18. for j in range(2, i + 1):
  19. dp[j] = dp[j-1] + dp[j-2]
  20. return dp[i]
  21. assert(fibo_memo(100) == fibo_dp(100))

Now time it:

  1. i = 10
  2. %timeit fibo_memo(i) # 73 ns
  3. %timeit fibo_dp(i) # 309 ns
  4. i = 100
  5. %timeit fibo_memo(i) # 73 ns
  6. %timeit fibo_dp(i) # 2.54 micro seconds
  7. i = 1000
  8. %timeit fibo_memo(i) # 73 ns
  9. %timeit fibo_dp(i) # 33 micro seconds

Why does memoization result in almost constant time, unlike dynamic programming?

答案1

得分: 2

"mutable default argument of fibo_memo"(fibo_memo的可变默认参数)在%timeit迭代之间保持不变,其中包含记忆化的值,因此除了第一次迭代外,所有迭代都几乎瞬间完成。 (实际上,对于i = 10i = 100,第一次迭代可能也几乎瞬间完成,因为assert检查将记忆化字典填充到100。)

为了进行一对一的比较,每次都应该使用空的记忆化字典来调用fibo_memo

  1. i = 10
  2. %timeit fibo_memo(i, {})
  3. %timeit fibo_dp(i)
  4. i = 100
  5. %timeit fibo_memo(i, {})
  6. %timeit fibo_dp(i)
  7. i = 1000
  8. %timeit fibo_memo(i, {})
  9. %timeit fibo_dp(i)

在我在Google Colab上的快速运行中,这显示出了两种解决方案的非常相似的缩放行为(大致符合预期的O(N),记忆化解决方案对于每个i值大约需要DP的3倍时间)。

英文:

The mutable default argument of fibo_memo, which holds the memoized values, persists between iterations of %timeit. So all but the first iteration complete near-instantly. (And in fact, for i = 10 and i = 100, the first iteration is probably near-instant too, since the assert check populated the memoization dict up to 100.)

For a like-for-like comparison, fibo_memo should be called with an empty memoization dict each time:

  1. i = 10
  2. %timeit fibo_memo(i, {})
  3. %timeit fibo_dp(i)
  4. i = 100
  5. %timeit fibo_memo(i, {})
  6. %timeit fibo_dp(i)
  7. i = 1000
  8. %timeit fibo_memo(i, {})
  9. %timeit fibo_dp(i)

In my quick run on Google Colab, this shows very similar scaling behaviour for both solutions (roughly the expected O(N), with the memoization solution taking about 3 times as long as the DP for each value of i).

huangapple
  • 本文由 发表于 2023年5月22日 04:59:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76301901.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定