我试图在递归的fibonacci函数中使用数组实现memoisation,fib()
期望运行时间以O(n)的形式出现。最初,它看起来好像我拥有它,因为它比常规的递归fibonacci函数运行要快得多。 (红色是常规fibmem()
,绿色是fibmem()
)
但经过进一步检查,(fibmem()
用红色表示)
看起来好像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
的数组实现在指数时间内运行。发生了什么事?我该如何解决问题?
答案 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