【问题标题】:Calculating nth fibonacci number modulo m using golden ratio in C使用C中的黄金比例计算第n个斐波那契数模m
【发布时间】:2016-04-17 19:35:50
【问题描述】:

我正在尝试计算第 n 个斐波那契数模 10^9+7,其中 n 由用户输入。

我使用黄金比例来计算斐波那契数。

以下代码在 n=43 之前产生正确的结果。但是对于 n>=44,phi 超过 10^9+7,我开始得到意想不到的结果。此外,如果去除模数,n>=44 会给出正确的结果。

#include <stdio.h>
#include <math.h>
long double mod=1000000007;

long double power(long double base, long long int expo)
{
    if(base==1  ||  expo==0)
        return 1;
    if(expo&1)
    {
        long double temp = power(base, expo>>1);
        return fmodl(base * fmodl(temp*temp, mod), mod);
    }
    else
    {
        long double temp=power(base, expo>>1);
        return fmodl(temp*temp,mod);
    }
}


int main(void) {
    // your code goes here
    long double phi = (1+powl(5, 0.5))/2;
    long double phi_cap = (1 - powl(5, 0.5))/2;
    long double root5 = powl(5, 0.5);
    long long int n;
    scanf("%lld",&n);
    long double ans = fmodl(  (power(phi, n) - power(phi_cap, n)) * power(root5,mod-2), mod);
    printf("%.0Lf\n", ans);
    return 0;
}

为什么会这样?用long double存储无理数有错吗?

谢谢

【问题讨论】:

  • 问题:用long double存储无理数有错吗?答:是的,因为它们只能在一定程度上精确。你可能想看看像 GMP 这样的库(参见 link)。
  • Modular congruence 不能应用于有理数,只能应用于整数。
  • @pytheos:谢谢。但我想它应该仍然适用于较低的值
  • @MOehm:谢谢。这是否意味着不可能使用黄金比例来做到这一点?您能否举例说明何时可以使用 fmodl() 和 fmod() 等函数?
  • 为什么要用这种方式计算斐波那契数?因为你认为它更快?

标签: c fibonacci modulo


【解决方案1】:

closed form expression for the n'th Fibonacci number 是

Fn   =   φn / √5   -   ψn / √5

在哪里

φ = 1/2 + √5/2 ≅ 1.6180339887
ψ = 1/2 - √5/2 = φ - 1 ≅ 0.6180339887

由于 Fn 中的减法部分总是小于二分之一,我们可以计算 Fn via rounding 趋向于零(或者,趋向于负无穷大,或 floor(),因为 Fn 对于 n ≥ 0 是非负数):

Fn = ⌊ φn / √5 ⌋ = floor( φn / √5 )

如果我们对Fn模M,M≥2感兴趣,我们需要观察modulo operation对上述公式的影响。请注意,表达式 "expr MOD M" 通常使用 C 中的fmod(expr, M) 计算。

我们可以应用property of modular multiplication:对于正数a、b和m,(a mod m)(b mod m) = (a b mod m)。地板操作不受影响。这里,a = φn,b = 1/√5。这意味着我们可以简化表达式

Fn MOD M = ⌊ φn / √5 ⌋ MOD M

进入

Fn MOD M = ⌊ ( φn MOD (M√5) ) / √5 ⌋

在这里,我们可以申请modular exponentiation,注意此时的模不是整数M,而是M·√5。

换句话说,如果我们有一个函数使用浮点模数的模幂来计算浮点值的整数幂,比如说

double pow_mod(double base, unsigned int exponent, double modulus);

我们可以计算第n个斐波那契数Fn模m使用

#define PHI    1.61803398874989484820458683436563811772
#define SQRT5  2.236067977499789696409173668731276235441;

double fn_mod_m;
unsigned int n, m;

fn_mod_m = floor(pow_mod(PHI, n, SQRT5 * (double)m) / SQRT5);

right-to-left method 是此处模幂运算的绝佳候选者。

【讨论】:

  • 您能解释一下您是如何从 mod M 转到 mod Mroot5 的吗?我尝试使用this method,但它可能只适用于整数?
  • @raksh93:我在答案中添加了解释;因为(a mod m)*(b mod m) = (a*b) mod m。 floor 运算不受影响,因为它只是从结果中减去一个小值(小于 1),移动模运算根本不会影响要 floor()ed 的值。至于模幂运算,你真的应该使用right-to-left method,而不是:维基百科页面甚至有伪代码来告诉你如何。或者你可以使用内存效率高的方法,在同一页面之前使用伪代码。
  • 模运算不能很好地处理 PHI 和 SQRT5 等实数。
猜你喜欢
  • 2018-08-18
  • 2016-01-05
  • 2017-07-07
  • 2017-01-29
  • 2013-02-09
  • 1970-01-01
  • 2022-12-10
  • 1970-01-01
  • 2012-08-19
相关资源
最近更新 更多