【问题标题】:What is the most computationally efficient method to recursively calculate the Fibonacci sequence?递归计算斐波那契数列的计算效率最高的方法是什么?
【发布时间】:2017-10-05 15:24:27
【问题描述】:

这是我目前拥有的代码。

def fibonacci(n):

    if n == 1:
        return 1
    elif n == 2:
        return 1
    else:
        value = fibonacci(n - 1) + fibonacci(n - 2)
        return value

目前这需要相当长的时间来计算大于 n = 30 的值。是否有更高效的计算方法来完成此操作?

【问题讨论】:

  • 是的。不要使用递归。

标签: python recursion fibonacci


【解决方案1】:

添加值缓存以交换一些内存以减少处理时间可能是一种有用的方法。纯粹的递归程序将尝试一遍又一遍地计算值,但是对于较大的值这需要时间。如果这些值没有改变,那么存储它们会很有帮助。然而,重要的是要注意,如果值是不稳定的,您可能需要一种不同的方法。

fibonacci_value_cache = {}


def fibonacci(n):

    if n == 1:
        return 1
    elif n == 2:
        return 1
    elif n in fibonacci_value_cache:
        return fibonacci_value_cache[n]
    else:
        fibonacci_value_cache[n] = fibonacci(n - 1) + fibonacci(n - 2)
        return fibonacci_value_cache[n]

n = 100

print("Fib " + str(n) + ": " + str(fibonacci(n)))

这里,我们检查该值是否在字典中,如果是则返回,否则我们计算并添加到字典中。这意味着我们可以通过不多次计算相同的值来更好地利用处理器。

【讨论】:

  • 额外提示:对于 Python 3 用户,lru_cache 装饰器可以方便地完成缓存值的所有工作,而无需您自己手动编写字典管理条件。
  • FWIW,如果您想要一种快速计算斐波那契数的方法,请参阅我的答案末尾附近的fast_fib here
  • 顺便说一句,最好让print(或format)为您处理字符串转换和连接,而不是手动进行。例如,print("Fib", n, fibonacci(n))print('Fib {} {}'.format(n, fibonacci(n)))。代码往往更紧凑,也更高效。
  • 您还有两个 if 分支,它们对可以很容易被单个条件覆盖的条件执行相同的操作。
  • 我想应该提到的是,虽然进行斐波那契计算可能是学习递归的一个很好的练习,但通常最好避免 Python 中的递归,除非你正在做一些实际需要递归的事情,例如处理递归数据结构,如树。 Python 无法优化tail calls,并且它的递归深度有一个限制(尽管您可以根据需要更改该限制)。
【解决方案2】:

有一个装饰器的配方,它以你想要的为例。它在PythonDecoratorLibrary 中被命名为Memoize

这看起来有点矫枉过正,但使用 memoized 装饰器可能对未来的其他任务很有用。也就是说,这里是它的全部内容(尽管我最后更改了print):

import collections
import functools

class memoized(object):
   '''Decorator. Caches a function's return value each time it is called.
   If called later with the same arguments, the cached value is returned
   (not reevaluated).
   '''
   def __init__(self, func):
      self.func = func
      self.cache = {}
   def __call__(self, *args):
      if not isinstance(args, collections.Hashable):
         # uncacheable. a list, for instance.
         # better to not cache than blow up.
         return self.func(*args)
      if args in self.cache:
         return self.cache[args]
      else:
         value = self.func(*args)
         self.cache[args] = value
         return value
   def __repr__(self):
      '''Return the function's docstring.'''
      return self.func.__doc__
   def __get__(self, obj, objtype):
      '''Support instance methods.'''
      return functools.partial(self.__call__, obj)

@memoized
def fibonacci(n):
   "Return the nth fibonacci number."
   if n in (0, 1):
      return n
   return fibonacci(n-1) + fibonacci(n-2)

print(fibonacci(12))

【讨论】:

    【解决方案3】:

    利用动态规划的思想,将中间结果存储起来,节省计算成本,效率很高。在我的笔记本电脑上,对于 n=10000,以下代码的成本低于 0.02s

    def fib(n):  # return Fibonacci series up to n
        result = []
        a, b = 0, 1
        for i in range(n):
            result.append(b)
            a, b = b, a + b
        return result
    

    【讨论】:

    • 您的代码不是递归地计算斐波那契数列,这是问题特别提出的问题,而不是以其他不同的方式进行。
    • Ops,没看到,虽然递归很昂贵。
    • 你应该在这里使用for循环。
    • 你说得对,使用 for 循环可以看起来更优雅。我正在修改它。
    • @juanpa:这里可能不是 for 循环。没有必要检查每个i 的值直到n,每次将其递增1。每次迭代至少可以增加到b。应该让它更快......
    【解决方案4】:

    不需要缓存/记忆。这是一个 Python 3 实现,它表达了Fibonacci sequence as powers of a matrix,然后通过减半和平方进行有效的求幂。结果在时间和存储上都是 O(log n)。

    def matrix_fib(n):
        if n == 1:
            return [0,1]
        else:
            f = matrix_fib(n // 2)
            c = f[0] * f[0] + f[1] * f[1]
            d = f[1] * (f[1] + 2 * f[0])
            return [c,d] if (n & 1) == 0 else [d,c+d]
    
    def fib(n):
      return n if n == 0 else matrix_fib(n)[1]
    
    print(fib(1000000))
    

    在我的笔记本电脑上,这会在半秒多一点的时间内计算出百万分之一的斐波那契数的值,其中大部分可能是输出的大整数运算和格式——结果大得离谱。不过,您不必担心堆栈溢出。调用堆栈深度仅为 log2(1000000) = 20。

    【讨论】:

      猜你喜欢
      • 2013-08-12
      • 1970-01-01
      • 1970-01-01
      • 2013-03-31
      • 2020-01-18
      • 1970-01-01
      • 1970-01-01
      • 2014-12-26
      • 2017-07-22
      相关资源
      最近更新 更多