如何在 C++ (g++) 中用 A,B<=10^18 计算 Fibonacci(A) mod B

How can I calculate Fibonacci(A) mod B with A,B<=10^18 in C++ (g++)

例如,A=10^17,B=10^17(<64 位)
通常,在上面的算法中,计算 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++)中的一些类型可以用来避免溢出吗?
我尝试了比 long double 更好的 __float128 但没有成功。 (见上面的 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;
}

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

计算不需要任何浮点类型。您只能使用 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>=mod)c-=mod(它们可能非常慢)。

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


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


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

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