【问题标题】:How can I calculate Fibonacci(A) mod B with A,B<=10^18 in C++ (g++) [closed]如何在 C++ (g++)中使用 A,B<=10^18 计算 Fibonacci(A) mod B [关闭]
【发布时间】:2017-09-27 13:34:33
【问题描述】:

例如,A=10^17, B=10^17 ( 通常,在上面的算法中,计算 F(2n) 和计算 F(2n+1) 的计算超过了 long long int 类型,我们不能在其中使用模计算。
我所说的计算它的最佳算法是斐波那契快速加倍:

F(0) = 0, F(1) = 1。
F(2n) = F(n)(2*F(n+1) – F(n))。
F(2n + 1) = F(n)2 + F(n+1)2.

你知道在新的 C++14(g++8.3.0 或 llvm-clang C++)中使用一些类型来避免溢出吗?
我试过 __float128 比 long double 好,但没有成功。 (参见上面的 g++ 代码)
我听说存在 __int128 和 __int256 没有 printf 的可能性,但我没有尝试过。
它们在 g++ 8.3.0 中是否可用,或者是否有其他快速方法来处理 128 位整数来进行您能想到的中间计算? (时间性能很重要)

#include <bits/stdc++.h>
using namespace std;

__float128 a,b,c,d;
long long mod;

void fast_fib(long long n,long long ans[]){
    if(n == 0){
        ans[0] = 0;
        ans[1] = 1;
        return;
    }
    fast_fib((n/2),ans);
    a = ans[0];             /* F(n) */
    b = ans[1];             /* F(n+1) */
    c = 2*b - a;
    if(c < 0) c += mod;
    c = (a * c);      /* F(2n) */
    while(c>=mod)c-=mod;
    d = (a*a + b*b);  /* F(2n + 1) */
    while(d>=mod)d-=mod;
    if(n%2 == 0){
        ans[0] = c;
        ans[1] = d;
    }
    else{
        ans[0] = d;
        ans[1] = c+d;
    }
}

int main(){
  int T=1000;
  long long n;
  while(T--){
    scanf("%lld %lld",&n,&mod);
    long long ans[2]={0};
    fast_fib(n,ans);
    printf("%lld\n", ans[0]);
  }
  return 0;
}

使用 __float128 我无法有效地实现模数,a、b、c、d 必须存储 128 位数据。

【问题讨论】:

标签: c++ algorithm math g++ implementation


【解决方案1】:

计算不需要任何浮点类型。您只能使用long long 类型。首先,您需要一个将两个long long 数字(小于10^18)以B 取模的函数。这可以通过类似于exponentiation by squaring 的方法来完成:

long long multiply(long long a, long long b, long long M) {
    long long res = 0;
    long long d = a;

    while (b > 0) {
        if (b & 1) {
            res = (res + d) % M;
        }
        b /= 2;
        d = (d + d) % M;
    }
    return res;
}

其次,您需要在几乎所有算术运算中添加模运算。您肯定需要在相应的操作中添加% mod 来替换这些循环while(c&gt;=mod)c-=mod(它们可能非常慢)。

__float_128 的代码替换为 long long 并使用适当的模运算:https://ideone.com/t6R7Tf


您可以做的另一件事是使用(如 cmets 中所述)Boost.Multiprecision 或非标准 __int128 类型(如果支持)而不是具有复杂乘法的 long long 类型。


此外,您可以使用稍微不同(但实际上使用相同的数学)的方法,这对我来说似乎更明显 - 斐波那契数矩阵公式

要计算矩阵的Nth 次方,您可以通过对所有以B 为模的运算进行平方来使用求幂。

【讨论】:

  • 这与我在帖子中提到的斐波那契快速加倍算法的问题相同。问题更多在于我可以用来计算它的类型。我见过 __float128 但我认为我不能用这个浮点数实现所有 int 变量。
  • 问题提到了g++。假设 x86_64,__int128 会使乘法更容易。或者使用 Boost.Multiprecision 类型。
  • 注意:这本质上与问题中提供的快速加倍算法相同,性能相同。
  • @JamesKPolk,没错。问题的较早版本没有包含任何代码或实现细节,我提出了对我个人来说似乎更明显的矩阵方法。
  • Here 是另一个有趣的表述。
猜你喜欢
  • 1970-01-01
  • 2015-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多