为什么这个记忆功能不能在线性时间内运行?

时间:2016-01-24 20:41:53

标签: python runtime big-o time-complexity memoization

我试图在递归的fibonacci函数中使用数组实现memoisation,fib()期望运行时间以O(n)的形式出现。最初,它看起来好像我拥有它,因为它比常规的递归fibonacci函数运行要快得多。 (红色是常规fibmem(),绿色是fibmem()

documentation

但经过进一步检查,(fibmem()用红色表示)

contains

看起来好像memo = [0] * 100 #initialise the array argument = sys.argv[1] def fibmem(n): if n < 0: return "NO" if n == 0 or n == 1: memo[n] = 1 return memo[n] if n not in memo: memo[n] = fibmem(n-1) + fibmem(n-2) return memo[n] 在O(someconstant ^ n)时间内运行。这是代码:

fibmem()

现在,我可以通过这种方式使用字典而不是数组让memo = {0:1, 1:1} argument = sys.argv[1] def fibmem(n): if n not in memo: memo[n] = fibmem(n-1) + fibmem(n-2) return memo[n] 在O(n)时间内运行:

fibmem()

但我认为我使用数组的实现是类似的。我只是不明白为什么format的数组实现在指数时间内运行。发生了什么事?我该如何解决问题?

2 个答案:

答案 0 :(得分:5)

真实问题不是in运算符扫描列表并占用线性时间,而是您完全错误

memo将填充[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...]。因此,当n例如是40,而您正在检查40 not in memo时,它只会始终失败,因为40不是斐波纳契数。显然,你的意思是要检查第40个Fibonacci数是否已经计算过,但这根本不是你实际检查的数字。而是检查40是否是(已计算的)斐波纳契数。

因此,只有当n本身恰好是斐波纳契数时才会获得快捷方式,例如在34处。但是直到55,你永远不会得到任何这样的捷径,完全有效地禁用你的记忆(在这些范围内)。这就是你在那里获得指数行为的原因,就像之前的非备忘版本一样。

还要注意在n = 35和n = 36之间的曲线中断。这不仅仅是一个侥幸,正是因为34是斐波纳契数。情况n = 36返回到n = 35并且n = 34,并且因为n = 34是即时快捷方式,所以只有n = 35部分涉及实际工作。这就是为什么n = 36几乎与n = 35几乎完全相同( 一个侥幸,当你测量它时,它略微更少)。

而不是if n not in memo:,您应该检查if memo[n] == 0:if not memo[n]:

或者使用字典:memo = {}。然后你的if n not in memo:做它应该做的事情(因为它检查键,而不是值)。这也有不受限制的优点。

答案 1 :(得分:2)

您的问题是列表上的in运算符从头到尾扫描列表。它并不神奇地知道你按顺序存放东西。

你可以使用内置的套装来解决这个问题。或者您可以使用数组查找来检查是否设置了数组值:

memo = [1,1] + [0]*98
def fibmem(n):
    answer = memo[n]

    if answer == 0:
        answer = fibmem(n-1) + fibmem(n-2)
        memo[n] = answer

    return answer