在C ++中准确计算Fibonacci数?

时间:2012-03-10 09:03:06

标签: c++ fibonacci

我真的很困惑。我正在尝试计算斐波纳契数,但随着它们越来越大,数字开始变得错误。我不知道为什么。

如何使用Binet公式计算准确的斐波纳契数,我的理解是这应该总是返回一个整数?

以下是我一直在尝试使用的内容。

http://ideone.com/e6t6h

看到数字上升。它变得奇怪了吗?

这里我用cout.precision(15)打印出来;

http://ideone.com/XREh2

这里我用cout打印出来<<固定<<等等;等等

这里我使用了一个程序循环来计算迭代次数。

这个比使用Binet公式更准确。

反正。剂量任何人都有任何我可以看到的代码可以计算F(n)而不需要通过使用Binet公式迭代每个级别的(n)?

3 个答案:

答案 0 :(得分:15)

要使用Binet公式准确计算斐波纳契数,您需要准确解释√5。由于√5是不合理的,因此无法使用doublefloat准确表示,因此Binet的公式不适用于这些类型(但是,计算中的舍入会导致某些小输入的精确结果)。由于Fibonacci数是整数,因此您可以通过使用doublefloat获得Binet公式的精确结果,通过舍入后得到更多参数,

double binet(unsigned int n)
{
    static const double phi = (1 + sqrt(5))*0.5;
    double fib = (pow(phi,n) - pow(1-phi,n))/sqrt(5);
    return round(fib);
}

这将为几乎所有n返回正确的结果,其结果可以精确地表示为double。然而,这些并不多。 double通常只有53位的精度,因此只有小于2 53 的斐波那契数可以精确地表示为double(加上一些可以被足够高整除的较大值)权力2)。最后一个小于2 53 的Fibonacci数是F(77),但是F(78)可以被8整除,因此也可以精确地表示为具有53位精度的double。但是,上面只为n <= 70产生了正确的结果,从71开始,舍入误差太大(顺便说一下,使用doubles的Binet公式的结果总是太大,所以使用{{ 1}}而不是floor也会为F(71)产生正确的结果,但是没有进一步的。)

对于标准数据类型,没有多少斐波纳契数是完全可表示的,最后一个适合(无符号)64位类型的是F(93);对于128位,最后一位是F(186)。对于如此小的指数,实际上没有什么可以通过直接的迭代算法获得

round

除非您使用查找表

unsigned long long fibonacci(unsigned int n)
{
    unsigned long long a = 0, b = 1;
    for(; n > 0; --n)
    {
        b += a;
        a = b-a;
    }
    return a;
}

为了获得准确的结果,必须将√5(和/或φ)视为符号常数,并使用它来评估公式。这相当于评估环中的公式

static const unsigned long long fibs[94] = { 0, 1, 1, 2, ... , 12200160415121876738ull };
使用ℤ[φ] = { a + b*φ : a, b ∈ ℤ } 这一事实,ℚ(√5)中的代数整数

。相当于Binet的公式是

φ² = 1 + φ

可用于通过O(log n)步骤中的重复平方来有效地计算Fibonacci数(但请注意F(n)具有Θ(n)位,因此位操作的数量不能低于O (N))。比香草重复平方使用

更高效的版本
φ^n = F(n-1) + φ*F(n)

使用φ^(2n) = (φ^n)² = (F(n-1) + φ*F(n))² = F(n-1)² + φ*2*F(n-1)*F(n) + φ²*F(n)² = (F(n-1)² + F(n)²) + φ*(2*F(n-1)*F(n) + F(n)²) 查找F(2n) = 2*F(n)*F(n-1) + F(n)² = 2*F(n)*F(n+1) - F(n)² = F(n)*(F(n+1) + F(n-1))F(2n+1) = F(n)² + F(n+1)²。这些公式允许从F(n)和F(n + 1)计算F(2n),F(2n + 1)和F(2n + 2),每个数最多两次乘法和两次加法/减法,这给出了在O(log n)步骤中计算对φ² = 1 + φ的算法,只有两个数作为状态(vanilla重复平方使用四个数作为状态,需要多一些乘法)。

迭代的从左到右算法

(F(n),F(n+1))

使用任意精度类型而不是unsigned long long fib(unsigned int n){ if (n == 0) return 0; unsigned int h = n/2, mask = 1; // find highest set bit in n, can be done better while(mask <= h) mask <<= 1; mask >>= 1; unsigned long long a = 1, b = 1, c; // a = F(k), b = F(k+1), k = 1 initially while(mask) { c = a*a+b*b; // F(2k+1) if (n&mask) { b = b*(b+2*a); // F(2k+2) a = c; // F(2k+1) } else { a = a*(2*b-a); // F(2k) b = c; // F(2k+1) } mask >>= 1; } return a; } ,可以快速计算大的Fibonacci数。但是当然任意精度库通常都带有自己优化的Fibonacci函数,因此自己实现它是没有意义的。

答案 1 :(得分:2)

通常,浮点数和双打数不能准确表示数字。它们的目的是在很大范围内表示实数。如果您确实需要无限精度,可以尝试查看http://gmplib.org/

答案 2 :(得分:1)

您是否尝试过包含<cmath>而不是<math.h>,math.h可能没有像C一样的sqrt的重载版本