【问题标题】:Python Fibonacci recursion (and not iteration) with O(n) time?具有O(n)时间的Python Fibonacci递归(而不是迭代)?
【发布时间】:2021-06-03 21:52:35
【问题描述】:

我知道我的 Python Fibonacci 算法的简单版本的时间复杂度为 O(2^n)^2:

def fibonacci_naive(n):
    if n < 0:
        return None
    if n == 0 or n == 1:
        return 0
    if n == 2:
        return 1
    return fibonacci_naive(n - 1) + fibonacci_naive(n - 2)

我正在尝试编写另一个斐波那契递归,但复杂度为 O(n)。到目前为止,我可以想到如何用一个简单的for 循环来做到这一点,但我无法将我的头转向如何将它变成一个递归函数。任何帮助表示赞赏!

def fibonacci_fast(n):
    if n < 0:
        return None
    if n == 0 or n == 1:
        return 0
    if n == 2:
        return 1
    fn_minus_2 = 0
    fn_minus_1 = 1
    for _ in range(2, n+1):
        temp = fn_minus_2 + fn_minus_1
        fn_minus_2 = fn_minus_1
        fn_minus_1 = temp
    return fn_minus_1
def fibonacci_fast(n, fn_minus_1=1, fn_minus_2=0):
    if n < 0:
        return None
    if n == 0 or n == 1:
        return 0
    if n == 2:
        return 1
    return fibonacci_fast(???)

【问题讨论】:

  • 斐波那契数列是为什么不使用递归并在实施前思考的主要示例之一。只需重复将最后两项相加即可轻松计算出序列,而某些定义可能会导致您从另一个方向进入更复杂的解决方案,包括完全成长的递归树。
  • 我认为你不能。您可以使用 memoizing 来缓存已计算的值并防止递归调用,但最好不要使用递归。
  • @NobbyNobbs 你绝对可以,你必须使用递归作为一个迭代过程
  • 顺便说一句,复杂度是 O(phi^n) IIRC。

标签: python recursion fibonacci


【解决方案1】:

您可以使用默认参数将序列向前移动并使用递归来倒数元素的数量:

def fibo(N,a=0,b=1): return fibo(N-1,b,a+b) if N else a

你可以用同样的方法来获取序列的前N个值:

def fibo(N,a=0,b=1): return [a] + fibo(N-1,b,a+b) if N else []

【讨论】:

  • 啊这个干净多了
【解决方案2】:

您需要使用迭代过程,例如:

def fib_r(n, depth=2, minus_1=1, minus_2=0):
    if n == 0:
        return 0
    elif n == 1:
        return 1

    elif depth < n:
        return fib_r(n=n, depth=depth+1, minus_1=minus_1 + minus_2, minus_2=minus_1)
    else:
        return minus_1 + minus_2

只是为了给你看,这里是和使用迭代的版本进行比较,基本上,递归充当了我们的循环:

In [1]: def fib(n):
   ...:     a, b = 0, 1
   ...:     for _ in range(n):
   ...:         a, b = b, a+b
   ...:     return a
   ...:

In [6]: def fibr(n, depth=2, a=1, b=0):
   ...:     if n == 0:
   ...:         return 0
   ...:     elif n == 1:
   ...:         return 1
   ...:     elif depth < n:
   ...:         return fibr(n=n, depth=depth+1, a=a+b, b=a)
   ...:     else:
   ...:         return a + b
   ...:
   ...:
    
In [3]: all(fib(i) == fibr(i) for i in range(1000))
Out[3]: True

所以,每个人都应该阅读 Ableson 和 Sussman 的 Structure and Interpretation of Computer Programs

其实 MIT 是免费提供 PDF 的:

https://web.mit.edu/alexmv/6.037/sicp.pdf

开始阅读第 1.2.1 节,了解线性递归过程与迭代递归过程(有效的解决方案)以及图递归(这是幼稚、低效的解决方案所使用的)。

【讨论】:

  • 哦,这很有道理! acc=None 参数有误吗?
  • 难道不能让 n 倒计时直到它等于 2 而不是使用深度吗?或者这没有意义......它会弄乱减号1和减号2吗?
  • 哦,是的,你是对的,如果我们从 n 开始并向后退,那么我们就不能真正使用默认值。好的,这行得通,谢谢!
  • @idontknowanygoodusernames 我提供了一个指向计算机科学开创性教科书的链接,该教科书解释了这一点。 SCIP。它使用scheme,LISP的一种方言作为教学语言,它没有循环,所以你必须使用递归。它讨论了线性递归过程、迭代递归过程(如高效递归 fibr)和树递归(朴素低效 fib 使用树递归)。它实际上在第 1.2.2 节中讨论了斐波那契,并介绍了两种解决方案!
【解决方案3】:

我想你可以从这篇文章中学到更多:

How to write the Fibonacci Sequence?

但无论如何我都会分享一个代码。

def fibonacci(n): 
      
    f = [0, 1] #first two nummber as 0, 1
     
    for i in range(2, n+1): #for i from range 2 to n+1
        f.append(f[i-1] + f[i-2])
    return f[n]

P/S:如果我没记错的话,递归斐波那契将永远是 O(2^n) / 指数

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-14
    • 2020-03-22
    • 1970-01-01
    • 2016-12-23
    • 2017-08-13
    • 2017-05-03
    • 1970-01-01
    • 2018-03-26
    相关资源
    最近更新 更多