【问题标题】:Fast Fibonacci recursion快速斐波那契递归
【发布时间】:2012-11-29 09:25:39
【问题描述】:

我正在尝试回忆关于斐波那契递归的算法。以下:

public int fibonacci(int n)  {
  if(n == 0)
    return 0;
  else if(n == 1)
    return 1;
  else
    return fibonacci(n - 1) + fibonacci(n - 2);
}

不是我正在寻找的东西,因为它很贪婪。这将呈指数级增长(只需查看Java recursive Fibonacci sequence - 初始参数越大,发出的无用调用就越多)。

可能存在类似“循环参数移位”的情况,调用先前的斐波那契值将检索值而不是再次计算它。

【问题讨论】:

    标签: algorithm recursion fibonacci


    【解决方案1】:

    假设你想要第 n 个 fib 数字,然后构建一个包含前面数字的数组

    int a[n];
    a[0] = 0;
    a[1] =1;
    a[i] = n[i-1]+n[n-2];
    

    【讨论】:

    • 有一种解决方案,无需将值存储在数组中。如果调用 f(n),每个数字 (n, n-1, n-2, ..., 1, 0) 将只计算一次。
    【解决方案2】:

    可能是这样的:

    int fib(int term, int val = 1, int prev = 0)
    {
     if(term == 0) return prev;
     return fib(term - 1, val+prev, val);
    }
    

    这个函数是尾递归的。这意味着它可以非常有效地优化和执行。事实上,它被优化成一个简单的循环..

    【讨论】:

    • 这正是我想要的。我不知道它在英语中被称为“尾递归”。非常感谢,伙计!
    • 或者你可以一开始就将它实现为一个循环,doh!
    • @TylerDurden:问题是关于快速递归。
    • 这仍然在 O(n) 中增长,您可以找到 O(log n) 更快的算法nayuki.io/page/fast-fibonacci-algorithms(链接在其他答案中)
    • 两个ifs 是多余的。你应该只有其中一个。
    【解决方案3】:

    您可以使用memoization 来做一个非常快速的递归斐波那契版本(意思是:存储以前的结果以避免重新计算它们)。例如,这是 Python 中的一个概念证明,其中使用字典来保存以前的结果:

    results = { 0:0, 1:1 }
    
    def memofib(n):
        if n not in results:
            results[n] = memofib(n-1) + memofib(n-2)
        return results[n]
    

    对于通常会阻塞“正常”递归版本的输入值,它会快速返回。请记住,int 数据类型不足以保存大型结果,建议使用任意精度整数。

    完全不同的选择 - 重写这个迭代版本...

    def iterfib(n):
        a, b = 0, 1
        for i in xrange(n):
            a, b = b, a + b
        return a
    

    ...作为尾递归函数,在我的代码中称为loop

    def tailfib(n):
        return loop(n, 0, 1)
    
    def loop(i, a, b):
        if i == 0:
            return a
        return loop(i-1, b, a+b)
    

    【讨论】:

    • @tkoomzaaskz 我用另一种可能的解决方案更新了我的答案,仅供参考。
    【解决方案4】:

    这类问题是线性递归类型,它们通过快速矩阵求幂最快地解决。这是简洁地描述这种方法的blogpost

    【讨论】:

      【解决方案5】:

      I found interesting article about fibonacci problem

      这里是代码 sn-p

      # Returns F(n)
      def fibonacci(n):
          if n < 0:
              raise ValueError("Negative arguments not implemented")
          return _fib(n)[0]
      
      
      # Returns a tuple (F(n), F(n+1))
      def _fib(n):
          if n == 0:
              return (0, 1)
          else:
              a, b = _fib(n // 2)
              c = a * (2 * b - a)
              d = b * b + a * a
              if n % 2 == 0:
                  return (c, d)
              else:
                  return (d, c + d)
      
      # added iterative version base on C# example
      def iterFib(n):
          a = 0
          b = 1
          i=31
          while i>=0:
              d = a * (b * 2 - a)
              e = a * a + b * b
              a = d
              b = e
              if ((n >> i) & 1) != 0:
                  c = a + b;
                  a = b
                  b = c
              i=i-1
          return a
      

      【讨论】:

      【解决方案6】:

      使用递归和延迟初始化缓存以提高效率的 JavaScript 示例:

      var cache = {};
      
      function fibonacciOf (n) {
        if(n === 0) return 0;
        if(n === 1) return 1;
        var previous = cache[n-1] || fibonacciOf(n-1);
        cache[n-1] = previous;
        return previous + fibonacciOf(n-2);
      };
      

      【讨论】:

        【解决方案7】:

        duedl0r 的算法翻译成 Swift:

        func fib(n: Int, previous: (Int, Int) = (0,1)) -> Int {
            guard n > 0 else { return 0 }
            if n == 1 { return previous.1 }
            return fib(n - 1, previous: (previous.1, previous.0 + previous.1))
        }
        

        工作示例:

        fib(4)
        = fib(4, (0,1) )
        = fib(3, (1,1) )
        = fib(2, (1,2) )
        = fib(1, (2,3) )
        = 3
        

        【讨论】:

          【解决方案8】:

          快速斐波那契计算的一个很好的算法是(在 python 中):

          def fib2(n):
              # return (fib(n), fib(n-1))
              if n ==  0: return (0,  1)
              if n == -1: return (1, -1)
              k, r = divmod(n, 2) # n=2k+r
              u_k, u_km1 = fib2(k)
              u_k_s, u_km1_s = u_k**2, u_km1**2  # Can be improved by parallel calls
              u_2kp1 = 4 * u_k_s - u_km1_s + (-2 if k%2 else 2)
              u_2km1 = u_k_s + u_km1_s
              u_2k   = u_2kp1 - u_2km1
              return (u_2kp1, u_2k) if r else (u_2k, u_2km1)
          
          def fib(n):
              k, r = divmod(n, 2) # n=2k+r
              u_k, u_km1 = fib2(k)
              return (2*u_k+u_km1)*(2*u_k-u_km1)+(-2 if k%2 else 2) if r else u_k*(u_k+2*u_km1)
          

          如果您需要非常快速的计算,请链接到 libgmp 并使用 mpz_fib_ui() 或 mpz_fib2_ui() 函数。

          【讨论】:

            【解决方案9】:

            您需要记住计算值以阻止指数增长。

            1. 只需使用数组来存储值。
            2. 如果您已经计算过数组,请检查它。
            3. 如果找到它,使用它或以其他方式计算并存储它。

            这是一个使用内存进行更快递归的工作示例。

            Calculating fibonacci number

            【讨论】:

              猜你喜欢
              • 2013-11-13
              • 2010-12-03
              • 2019-04-25
              • 2014-04-02
              • 2017-11-18
              • 2014-01-08
              • 2011-12-14
              • 2018-04-10
              相关资源
              最近更新 更多