【问题标题】:How much time does fibonacci algorithm take to compute F(n)斐波那契算法计算 F(n) 需要多少时间
【发布时间】:2016-12-02 12:33:05
【问题描述】:

我正在阅读 Skiena 的“算法设计手册”一书中的“8.1.1 Fibonacci numbers by recursion”部分。

我无法理解本节的以下段落。

这个算法需要多少时间来计算 F(n)?由于 Fn+1/Fn ≈ φ = (1+√5)/2 ≈ 1.61803,这意味着 Fn > 1.6^n。由于我们的递归树有 只有 0 和 1 作为叶子,加起来这么大的数字意味着我们必须有 至少 1.6n 个叶子或过程调用!这个不起眼的小程序需要指数级的时间来运行!

谁能解释一下我在这一段中提出的以下问题。

  • 为什么用Fn+1/Fn来计算算法时间?
  • 为什么 Fn > 1.6^n
  • 我们将如何获得 1.6n 个叶子或过程调用?

请以F(4)为例说明

【问题讨论】:

  • 我投票决定将此问题作为离题结束,因为它与computer science
  • 朴素斐波那契递归的复杂性是...斐波那契!
  • 看看here 了解处理斐波那契计算的有趣方式。
  • 哪种斐波那契算法?

标签: algorithm fibonacci


【解决方案1】:

这是我对我的问题的回答。 如果使用递归计算斐波那契数,计算时间为F(N)+F(N-1)+F(N-2)+...+F(1) = F(N+2)-2 = O(F(N+2)).
Binet's Formula 证明F(N) nearly equal sqrt(1/5) * φ^N,这个公式也证明了这一点。

  • F(k+1)/F(k) 几乎等于 φ。
  • 如果检查小 k,则可以证明 F(k) >= 1.6^k。

但我建议为这个算法计算斐波那契数。

1.使用动态规划
斐波那契数列可以进行动态规划计算,时间为 O(N)。
很明显,因为存在 F(N)=F(N-1)+F(N-2) 的关系。

2.使用矩阵指数
其实,( (1 1) (1 0) )^N = ( (F(N+2) F(N+1)) (F(N+1) F(N)) ).
如果使用平方乘幂算法,则可以计算 O(log N) 的值,因此可以计算 O(log(N)) 的 F(N)。

综上所述,Binet 的公式不好,因为它使用浮点值,所以会导致精度错误。
我建议使用动态规划或矩阵指数对这个问题有好处。

【讨论】:

  • 建议更快的算法并不能回答问题。
  • @tobias_k 我编辑并回答了问题。
  • 不要忘记简单的迭代,这比动态规划或递归方法要好得多。
  • 时间还不够O(N),因为数字本身会变大。
  • 我假设计算基本算术运算可以在 O(1) 中完成。
【解决方案2】:

对于答案的第一部分,我从 n 切换到 m。

F(m+1)/F(m) 是用于获得 F(m) 的近似值的比率,而不是时间。从 m >= 1 开始,随着 m 的增加,比率 F(m+1)/F(m) 迅速收敛到 φ = (1+sqrt(5))/2 ~= 1.61803。这可以重新表述为 F(m+1) ~= φ F(m)。那么 F(m+2) ~= φ F(m+1) ~= φ (φ F(m)) ~= φ^2 F(m),一般来说 F(m+k) ~= φ^k F(m),m >= 10 的合理近似值,如本答案末尾的表格所示。然后该段落突然跳转到 F(n) > 1.6^n 的陈述,这仅适用于 n >= 72。

该段然后处理递归树,它只涉及加法,并指出递归树的叶节点仅返回 0 (F(0)) 或 1 (F(1)),因此您至少需要1.6^n(不是 1.6n)叶节点(1.6^n 返回 1 的叶节点)产生总和 >= 1.6^n。 (再次注意 F(n) > 1.6^n 仅适用于 n >= 72)。

作为一种更快的算法,卢卡斯序列方法类似于通过平方方法进行优化的矩阵求幂。 64 位无符号整数的最大值是 fib(93) == 12200160415121876738(在下面的代码中需要 7 个循环)。

/* lucas sequence method */
uint64_t fib(uint64_t n) {
    uint64_t a, b, p, q, aq, qq;
    a = q = 1;
    b = p = 0;
    while(1){
        if (n & 1) {
            aq = a*q;
            a = b*q + aq + a*p;
            b = b*p + aq;
        }
        n >>= 1;
        if (n == 0)
            break;
        qq = q*q;
        q = p*q*2 + qq;
        p = p*p + qq;
    }
    return b;
}

为了了解近似值 F(m+k) ~= φ^k F(m) 的准确度,使用 F(100) 作为测试用例与 m (10, 20, 30, 40) 的值)。

F(100)/(φ^90 F(10)) ~= 1.0000661
F(100)/(φ^80 F(20)) ~= 1.00000000437
F(100)/(φ^70 F(30)) ~= 1.000000000000289
F(100)/(φ^60 F(40)) ~= 1.0000000000000000191

【讨论】:

    猜你喜欢
    • 2014-12-16
    • 1970-01-01
    • 2013-03-31
    • 1970-01-01
    • 2016-08-04
    • 1970-01-01
    • 1970-01-01
    • 2020-01-18
    • 2016-09-11
    相关资源
    最近更新 更多