【问题标题】:Why is one of these two algorithms for finding the n-th Fibonacci number more efficient?为什么这两种寻找第 n 个斐波那契数的算法中的一种更有效?
【发布时间】:2019-04-21 03:24:10
【问题描述】:

在计算第 64 个斐波那契数时,第一个算法需要几个小时,而第二个算法需要不到一秒。

为什么第二种算法的效率比第一种高很多?

它们看起来非常相似。

def fib_divide_recursion(n):
    if n <= 2:
        return n-1
    else:
        return fib_divide_recursion(n-1) + fib_divide_recursion(n-2)

def fib_linear_recursion(n, prev={}):
    if n <= 2:
        return n-1
    try:
        return prev[n]
    except KeyError:
        prev[n] = fib_linear_recursion(n - 1, prev) + 
            fib_linear_recursion(n - 2, prev)
        return prev[n]

【问题讨论】:

  • 因为第一个做更多的工作。计算它执行的函数调用次数。
  • 评估fib_divide_recursion(25)调用函数150049次,评估fib_linear_recursion(25)调用它47次,随着n的增长,差异会迅速恶化。
  • 除此之外,因为您将递归用于最常见的迭代编程示例。
  • @Klaus 是的,但递归学习斐波那契也很有价值 - 事实上,它是最常见的递归介绍程序之一。
  • @Klaus 好吧,我同意你的观点,但不幸的是,很难抵消先例的浪潮。无论如何,学习斐波那契对于理解递归与迭代的优缺点很有用,即使它并不理想,仅仅是因为它已经非常成熟了。

标签: python algorithm fibonacci


【解决方案1】:

第二种实现是使用“记忆”来记住以前计算的斐波那契值。

假设您尝试计算fib(5):您首先必须计算fib(4)fib(3)fib(4) 本身也需要你计算fib(3)。事实上,对于每个斐波那契数,您可以计算每个前面的斐波那契数一次并存储它们(这是记忆方法)。或者,在性能更差的情况下,您可以重新计算所需的每个斐波那契数,即使您之前已经计算过了。显然,如果没有记忆,您将需要做更多的工作,而对于高斐波那契数,这确实会产生影响,正如您所观察到的那样。

【讨论】:

  • 谢谢,为什么我不能用prev=[]替换prev{},即使我把KeyError也改成了IndexError
  • @longchaos 真的,这是一个不同的问题,如果你想正确回答,你应该单独发布。但简短的回答是列表不是字典:您不能索引到列表中尚不存在的部分并期望它会自动创建。你必须用你需要的元素数量来初始化它,这是不可扩展的。
【解决方案2】:

第一种算法的复杂度是O(2^n)。

第二个将结果缓存在prev 中,因此它不会对给定数字多次计算fib_linear_recursion。它的复杂度是线性的,O(n)。

更多详情请见this answer

【讨论】:

    【解决方案3】:

    第一种算法仅使用递归,而第二种算法使用动态编程,即带有记忆的递归。

    如果您为第一个算法绘制树,您会看到重复的节点。但是使用第二种算法,它存储已经计算的节点,因此程序不必一次又一次地计算

    【讨论】:

      猜你喜欢
      • 2022-12-10
      • 1970-01-01
      • 2014-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-22
      • 2016-11-28
      相关资源
      最近更新 更多