【问题标题】:Can a Fibonacci function be written to execute in O(1) time?可以编写斐波那契函数以在 O(1) 时间内执行吗?
【发布时间】:2011-08-27 14:31:55
【问题描述】:

所以,我们看到了很多斐波那契问题。我个人讨厌他们。很多。不止很多。我认为如果我们可以让任何人都无法再次将其用作面试问题,那就太好了。让我们看看如何接近 O(1) 我们可以得到斐波那契。

这是我的开场白,几乎是从维基百科抄来的,当然还有足够的空间。重要的是,这个解决方案对于任何特别大的 fib 都会引爆,并且它包含对幂函数的相对幼稚的使用,如果你的库不好,它在最坏的情况下将其置于 O(log(n))。我怀疑我们可以摆脱幂函数,或者至少专门化它。有人来帮忙吗?除了使用查找表的有限*解决方案之外,是否存在真正的 O(1) 解决方案?

http://ideone.com/FDt3P

#include <iostream>
#include <math.h>
using namespace std; // would never normally do this.

int main()
{
int target = 10;
cin >> target;
// should be close enough for anything that won't make us explode anyway.
float mangle = 2.23607610; 

float manglemore = mangle;
++manglemore; manglemore = manglemore / 2;
manglemore = pow(manglemore, target);
manglemore = manglemore/mangle;
manglemore += .5;
cout << floor(manglemore);

}

*我知道,我知道,对于斐波那契的任何零实际用途来说,这已经足够了。

【问题讨论】:

  • 这依赖于幂函数,不是 O(c)。我的例子实际上就是那个算法。这是提到的。在我的问题中。
  • 似乎最大的问题是 pow 函数,因为它不精确。也许可以将其拆分为任何错误都小于 1/2 然后舍入?然后重复? (使用单行数学方法得到第n个斐波那契)
  • 当然 - 只需使用查找表 - 在 1 和 FLT_MAX 之间没有那么多 斐波那契数。 ;-)
  • I'll make it O(1)... O(c) 是常数时间,用于表示它可能不是单个操作。不过看起来并不标准,所以...
  • 如果你想要一个简单的检查,斐波那契序列的最后一位数字形成一个模式(基数 16 每 24 次重复,基数 32 每 48 次重复,基数 64 每 96 次重复等)你可以用它来进行更准确的舍入。

标签: algorithm floating-point time-complexity fibonacci


【解决方案1】:

这是斐波那契数列项的近似O(1) 解决方案。诚然,O(log n) 取决于系统 Math.pow() 实现,但如果你的面试官正在寻找它,它是没有可见循环的斐波那契。 ceil() 是由于返回 0.9 重复的较大值的舍入精度。

JS 中的示例:

function fib (n) {
  var A=(1+Math.sqrt(5))/2,
      B=(1-Math.sqrt(5))/2,
      fib = (Math.pow(A,n) - Math.pow(B,n)) / Math.sqrt(5);
      return Math.ceil(fib);
}

【讨论】:

  • 不错。适用于 n
  • 谢谢你;我正在寻找该算法的代码以添加到课程演示中。但是,我们使用 Java (1.8) 并且直接翻译这使得样本值(例如 10、20、40、45、50)出来 1 太大。删除 .ceil() 调用修复了它。
  • 谁能解释一下为什么你需要在其他语言中使用 floor 函数?我查看了证据,没有任何明显的原因。
  • 这应该是最好的答案
  • 实际上你不必计算 B^n 从而使过程更快一点。 B^n 始终小于 0.5,因此您可以将公式重写为 Fib(n) = round(A^n/sqrt(5))en.wikipedia.org/wiki/Fibonacci_number#Computation_by_rounding
【解决方案2】:

给定任意大的输入,简单地读取 n 需要 O(log n),因此从这个意义上说,不可能有恒定时间算法。因此,使用封闭形式的解决方案,或预先计算您关心的值,以获得合理的性能。

编辑:在 cmets 中有人指出它实际上更糟,因为斐波那契是 O(phi^n) 打印斐波那契的 结果O(log (phi^n)) 这是 O(n)!强>

【讨论】:

  • 我猜现在可以了。只是感觉有点跛行。
  • 其实比这还要糟糕……没错,读取输入是 O(log n)。但是打印(甚至存储)输出是O(n),无论你使用什么基数。 (输出值为O(phi^n),需要O(n)个数字来表示。)
【解决方案3】:

以下答案在 O(1) 中执行,但我不确定它是否适合您的问题。它被称为Template Meta-Programming

#include <iostream>
using namespace std;

template <int N>
class Fibonacci
{
public:
    enum {
        value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value
    };
};

template <>
class Fibonacci<0>
{
public:
    enum {
        value = 0
    };
};

template <>
class Fibonacci<1>
{
public:
    enum {
        value = 1
    };
};

int main()
{
    cout << Fibonacci<50>::value << endl;
    return 0;
}

【讨论】:

  • 对于nitpick,它在编译时执行,它不需要固定时间。
  • @hammar,你不能否认它在 O(1)执行。它在 O(1) 时无法编译是另一回事。
  • 我的意思是这是一个元程序,元程序在编译时执行。在运行时,它只显示一个常数,实际上是 O(1)。
  • 它仍然非常聪明,即使它不适合我们的目的。
  • 这不是一个解决方案,而是您的编译器程序无法解决该任务,如果您说您通过编译找到,那么评论者是对的,因为它在编译时间不是 O(1)。
【解决方案4】:

Programming: The Derivation of Algorithms中,Anne Kaldewaij 扩展了the linear algebra solution 以获得(从该书中使用的编程语言翻译和重构):

template <typename Int_t> Int_t fib(Int_t n)
{
    Int_t a = 0, b = 1, x = 0, y 1, t0, t1;
    while (n != 0) {
        switch(n % 2) {
            case 1:
                t0 = a * x + b * y;
                t1 = b * x + a * y + b * y;
                x = t0;
                y = t1;
                --n;
                continue;
            default:
                t0 = a * a + b * b;
                t1 = 2 * a * b + b * b;
                a = t0;
                b = t1;
                n /= 2;
                continue;
        }
    }
    return x;
}

这有 O(log n) 复杂度。当然,这不是恒定的,但我认为值得加入讨论,特别是考虑到它只使用相对快速的整数运算并且没有舍入错误的可能性。

【讨论】:

    【解决方案5】:

    是的。预先计算值,并存储在数组中, 然后使用 N 进行查找。

    【讨论】:

    • 我确实提到了一个查找表。
    【解决方案6】:

    选择一些最大值来处理。对于任何更大的值,引发错误。对于任何小于该值的值,只需将答案存储在该较小值处,然后继续运行“最大”值的计算,然后返回存储的值。

    毕竟,O(1) 特指“恒定”,而不是“快速”。使用这种方法,所有计算都将花费相同的时间。

    【讨论】:

      【解决方案7】:

      O(1) 空间和时间中的斐波那契(Python 实现):

      PHI = (1 + sqrt(5)) / 2
      
      def fib(n: int):
        return int(PHI ** n / sqrt(5) + 0.5)
      

      【讨论】:

      • 你能测试一下哪些参数需要 5 秒、10、15、20、30、50、70、100 秒吗?这看起来是线性的吗?结果准确吗?
      猜你喜欢
      • 2019-10-22
      • 2020-01-16
      • 2013-10-30
      • 1970-01-01
      • 2014-05-03
      • 2017-01-08
      • 1970-01-01
      • 2010-09-22
      • 1970-01-01
      相关资源
      最近更新 更多