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

Posted

技术标签:

【中文标题】如何在 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++) [closed] 【发布时间】: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 位数据。

【问题讨论】:

Storing numbers with higher precision in C的可能重复 “Fibonnacci(A) 超过 long long int 类型”——无关紧要,您只需首先对 B 取模即可。 @harold +1:然后,这是 mathoverflow 的问题。 Huge fibonacci modulo m C++ 的可能重复项,或者您可以选择其他算法 清晰的解释(但没有代码):***.com/questions/40556760/… 【参考方案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 是另一个有趣的表述。

以上是关于如何在 C++ (g++)中使用 A,B<=10^18 计算 Fibonacci(A) mod B [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C++ 提取图像中的 r g b 颜色分量

2015级C++第4周项目 函数

使用 g++ 编译器打印 C++ 对象的布局

PTA basic 1086 就不告诉你 (15 分) c++语言实现(g++)

如何在c ++中增加字母?

Visual C++中二级指针的定义,初始化,赋值问题