动态编程:递归+记忆vs循环+列表

时间:2018-12-12 10:03:53

标签: python recursion dynamic-programming memoization

@functools.lru_cache的文档提供了一个示例,该示例使用缓存来实现动态编程技术来计算斐波那契数:

console.dir(nativeElement);

我已经看到了用于解决各种编程难题的方法。它是否具有与“标准”迭代动态编程方法相同的时间/空间复杂度,例如:

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)

还有,使用递归方法是否有缺点?

2 个答案:

答案 0 :(得分:3)

它应该与memoization(自上而下)的动态编程具有相同的复杂性。

使用逐步表填充的迭代方法(自底向上动态编程)的复杂度可能略有不同,因为备忘录仅记住构建最终解决方案所需的参数集

对于斐波纳契或阶乘示例而言,这种差异并不重要,但对于子表稀疏的任务(可能难以预测以后将使用哪些条目),可能会发生这种差异

答案 1 :(得分:0)

以下内容比较了您显示的功能。通常,这些观察结果不一定适用于任何递归或迭代方法来计算斐波那契数,而仅适用于您在问题中显示的实现。

时间复杂度:

首次通话

  • 递归方法:O(n)
  • 迭代方法:O(n)

后续通话

  • 递归方法:O(1)
  • 迭代方法:O(n)

恒定因素

恒定因素可能完全不同。尽管两者都是O(n),但(第一次调用)迭代方法可能会比递归方法更快。根据我的经验(不是实际测试),这只是一个猜测,列表索引比调用函数要快得多。

内存复杂度:

两者都需要O(n)额外的内存,但是递归方法将保留内存(因此它是永久分配的),而迭代方法将在函数完成后释放内存。

其他差异

Python的递归限制。如果时间过长且缓存不足,则递归方法将因该限制而失败,例如fib(500)。列表索引没有这种限制(内存不足时除外)。