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

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: 做它应该做的(因为它检查键,而不是值)。那也有不受限制的好处。