为什么这个记忆功能在线性时间内不是 运行?
Why does this memoisation function not run in linear time?
我尝试在递归斐波那契函数中使用数组实现记忆,fibmem()
期望 运行ninng 时间为 O(n)。最初,它看起来好像我有它,因为它比 运行 的常规递归斐波那契函数要快得多。 (红色是常规 fib()
,绿色是 fibmem()
)
但进一步观察,(fibmem()
用红色表示)
看起来好像 fibmem()
运行s 在 O(someconstant^n) 时间内。这是代码:
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(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]
但我认为我使用数组的实现是相似的。我只是不明白为什么 fibmem()
运行s 的数组实现在指数时间内。这是怎么回事?我将如何解决问题?
您的问题是 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
真正的问题不是in
运算符扫描列表并花费线性时间而是你完全错了.
您的 memo
将被 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...]
填充。因此,例如当 n
为 40 时,您正在检查 40 not in memo
,它只会 总是失败 ,因为 40 不是斐波那契数。显然,您的意思是检查是否已经计算出第 40 个斐波那契数,但这根本不是您实际检查的内容。您要检查 40 是否是(已计算的)斐波那契数。
所以只有当 n
本身恰好是斐波那契数时,例如在 34 时,您才会获得捷径。但是直到 55,您永远不会获得任何这样的捷径,有效地禁用您的完全记忆(在这些范围内)。这就是为什么你在那里得到指数行为,就像 non-memoized 版本早些时候所做的那样。
另请注意,n=35 和 n=36 之间的曲线明显中断。这不仅仅是侥幸,正是因为 34 是斐波那契数列。 n=36 的情况可以追溯到 n=35 和 n=34,因为 n=34 是一个即时捷径,只有 n=35 部分涉及实际工作。这就是为什么 n=36 与 n=35 花费几乎完全相同的时间(它 是 一个侥幸,当你测量它时它花费了稍微 less ).
而不是 if n not in memo:
你应该检查 if memo[n] == 0:
或 if not memo[n]:
.
或者,使用字典:memo = {}
。然后你的 if n not in memo:
做它应该做的(因为它检查键,而不是值)。那也有不受限制的好处。
我尝试在递归斐波那契函数中使用数组实现记忆,fibmem()
期望 运行ninng 时间为 O(n)。最初,它看起来好像我有它,因为它比 运行 的常规递归斐波那契函数要快得多。 (红色是常规 fib()
,绿色是 fibmem()
)
但进一步观察,(fibmem()
用红色表示)
看起来好像 fibmem()
运行s 在 O(someconstant^n) 时间内。这是代码:
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(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]
但我认为我使用数组的实现是相似的。我只是不明白为什么 fibmem()
运行s 的数组实现在指数时间内。这是怎么回事?我将如何解决问题?
您的问题是 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
真正的问题不是in
运算符扫描列表并花费线性时间而是你完全错了.
您的 memo
将被 [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...]
填充。因此,例如当 n
为 40 时,您正在检查 40 not in memo
,它只会 总是失败 ,因为 40 不是斐波那契数。显然,您的意思是检查是否已经计算出第 40 个斐波那契数,但这根本不是您实际检查的内容。您要检查 40 是否是(已计算的)斐波那契数。
所以只有当 n
本身恰好是斐波那契数时,例如在 34 时,您才会获得捷径。但是直到 55,您永远不会获得任何这样的捷径,有效地禁用您的完全记忆(在这些范围内)。这就是为什么你在那里得到指数行为,就像 non-memoized 版本早些时候所做的那样。
另请注意,n=35 和 n=36 之间的曲线明显中断。这不仅仅是侥幸,正是因为 34 是斐波那契数列。 n=36 的情况可以追溯到 n=35 和 n=34,因为 n=34 是一个即时捷径,只有 n=35 部分涉及实际工作。这就是为什么 n=36 与 n=35 花费几乎完全相同的时间(它 是 一个侥幸,当你测量它时它花费了稍微 less ).
而不是 if n not in memo:
你应该检查 if memo[n] == 0:
或 if not memo[n]:
.
或者,使用字典:memo = {}
。然后你的 if n not in memo:
做它应该做的(因为它检查键,而不是值)。那也有不受限制的好处。