具有记忆功能的斐波那契大 O?
Big-O of Fibonacci with memoization?
当我 运行 这个程序时,它似乎是 O(1) 因为它几乎是用于 fib 的相当大的数字而没有记忆。如果它正在计算之前的数字,那么它所要做的就是相加,结果为 O(1)?
memo = {}
def Fib(n):
if (n < 2):
return 1
if not n in memo:
memo[n] = Fib(n-1) + Fib(n-2)
return memo[n]
我也将它与没有记忆的斐波那契程序进行了比较,这是从 1 到 40 的绘图结果:
我的感觉还是O(n),假设你计算Fin(100),你需要计算所有数字才能得到100。当然如果前面的运行已经完成,然后你会把它记在内存中,但是对于你能想象到的任何数字,并且之前执行过 运行,我可以想象一个更大的数字:)
也许你可以说它是 O(1) 摊销的(与 java 中的 ArrayList 获取元素的 O(1) 相同......实际上它是 O(n) 作为您可能需要调整数组的大小,但通常情况并非如此,因此 O(1) 是一个 "acceptable" 度量)。
首先,你的算法的下限是 O(n)
,因为对于给定的 n
,你用 n
值填充字典(假设我们正在处理第一个调用至 Fib
).
另一方面,对于每个 n
,您在 Fib
函数中执行的每个操作都被 O(1)
摊销。所以总而言之,你得到 第一次调用 到 Fib
是 O(n)
摊销。
请注意,对于巨大的 n
,这可能会超过 O(n)
,因为 not in
操作不是 O(1)
(它只是 O(1)
摊销)。 n
有多大?不知道,取决于底层的哈希函数。另外,在达到 n
.
之前,您可能 运行 内存不足
现在这显然是以 space(即内存)为代价的,它也变成了 O(n)
。这只是假设每个整数都采用相同数量的 space,不幸的是,Python 并非如此。 "no limit for integers" 方法的结果是大整数作为数字数组保存在内存中。由于数字 n
最多有 log_b(n)+1
位(其中 b
是数字系统,例如 10
表示十进制,我不确定哪个 Python 使用在内部)我们得到真正的 space 复杂度介于 O(n)
和 O(log_b(n!))
.
之间
如果我们不关心它是否是第一次调用,事情就变得更复杂了。但只有一点点。您可以轻松检查 Fib
通常是 O(max(n-k, 1))
复杂性,其中 k
是调用时 memo
字典的大小。
例如将其与迭代方法进行比较。在那种方法中,你总是保留 2 个最后的元素和一个计数器。这样你就得到 O(n)
时间复杂度和 O(1)
space 复杂度。
当然,对于朴素的递归斐波那契,时间复杂度是 O(2^n)
和 O(n)
space 复杂度(由于调用堆栈)。
当我 运行 这个程序时,它似乎是 O(1) 因为它几乎是用于 fib 的相当大的数字而没有记忆。如果它正在计算之前的数字,那么它所要做的就是相加,结果为 O(1)?
memo = {}
def Fib(n):
if (n < 2):
return 1
if not n in memo:
memo[n] = Fib(n-1) + Fib(n-2)
return memo[n]
我也将它与没有记忆的斐波那契程序进行了比较,这是从 1 到 40 的绘图结果:
我的感觉还是O(n),假设你计算Fin(100),你需要计算所有数字才能得到100。当然如果前面的运行已经完成,然后你会把它记在内存中,但是对于你能想象到的任何数字,并且之前执行过 运行,我可以想象一个更大的数字:)
也许你可以说它是 O(1) 摊销的(与 java 中的 ArrayList 获取元素的 O(1) 相同......实际上它是 O(n) 作为您可能需要调整数组的大小,但通常情况并非如此,因此 O(1) 是一个 "acceptable" 度量)。
首先,你的算法的下限是 O(n)
,因为对于给定的 n
,你用 n
值填充字典(假设我们正在处理第一个调用至 Fib
).
另一方面,对于每个 n
,您在 Fib
函数中执行的每个操作都被 O(1)
摊销。所以总而言之,你得到 第一次调用 到 Fib
是 O(n)
摊销。
请注意,对于巨大的 n
,这可能会超过 O(n)
,因为 not in
操作不是 O(1)
(它只是 O(1)
摊销)。 n
有多大?不知道,取决于底层的哈希函数。另外,在达到 n
.
现在这显然是以 space(即内存)为代价的,它也变成了 O(n)
。这只是假设每个整数都采用相同数量的 space,不幸的是,Python 并非如此。 "no limit for integers" 方法的结果是大整数作为数字数组保存在内存中。由于数字 n
最多有 log_b(n)+1
位(其中 b
是数字系统,例如 10
表示十进制,我不确定哪个 Python 使用在内部)我们得到真正的 space 复杂度介于 O(n)
和 O(log_b(n!))
.
如果我们不关心它是否是第一次调用,事情就变得更复杂了。但只有一点点。您可以轻松检查 Fib
通常是 O(max(n-k, 1))
复杂性,其中 k
是调用时 memo
字典的大小。
例如将其与迭代方法进行比较。在那种方法中,你总是保留 2 个最后的元素和一个计数器。这样你就得到 O(n)
时间复杂度和 O(1)
space 复杂度。
当然,对于朴素的递归斐波那契,时间复杂度是 O(2^n)
和 O(n)
space 复杂度(由于调用堆栈)。