大整数的实现与性能 我已经写了C++中的一个大整数类,它应该能够对任何大小的所有数字进行运算。目前,我正试图通过比较现有的算法并测试它们最适合的位数来实现一种非常快速的乘法方法,我遇到了非常意外的结果。我尝试对500位数进行20次乘法,并计时。结果是: karatsuba: 14.178 seconds long multiplication: 0.879 seconds

大整数的实现与性能 我已经写了C++中的一个大整数类,它应该能够对任何大小的所有数字进行运算。目前,我正试图通过比较现有的算法并测试它们最适合的位数来实现一种非常快速的乘法方法,我遇到了非常意外的结果。我尝试对500位数进行20次乘法,并计时。结果是: karatsuba: 14.178 seconds long multiplication: 0.879 seconds,c++,performance,algorithm,implementation,biginteger,C++,Performance,Algorithm,Implementation,Biginteger,维基百科告诉我 因此,对于足够大的n,Karatsuba的算法执行的移位和单位数加法比徒手乘法要少,即使其基本步骤使用的加法和移位比简单的公式多。但是,对于较小的n值,额外的移位和加法操作可能会使其运行速度比直接法慢。正回报点取决于计算机平台和环境。根据经验,当被乘数大于320–640位时,Karatsuba通常速度更快 因为我的数字至少有1500位长,这是非常出乎意料的,因为维基百科说karatsuba应该跑得更快。我相信我的问题可能出在加法算法上,但我不知道如何使它更快,因为它已经在O(n)

维基百科告诉我

因此,对于足够大的n,Karatsuba的算法执行的移位和单位数加法比徒手乘法要少,即使其基本步骤使用的加法和移位比简单的公式多。但是,对于较小的n值,额外的移位和加法操作可能会使其运行速度比直接法慢。正回报点取决于计算机平台和环境。根据经验,当被乘数大于320–640位时,Karatsuba通常速度更快

因为我的数字至少有1500位长,这是非常出乎意料的,因为维基百科说karatsuba应该跑得更快。我相信我的问题可能出在加法算法上,但我不知道如何使它更快,因为它已经在O(n)中运行了。我将在下面发布我的代码,以便您检查我的实现。我会把不相关的部分撇开。
我也在想,也许我使用的结构不是最好的。我用little endian表示每个数据段。例如,如果我将数字12345678910112存储到长度为3的数据段中,它将如下所示:

{112101789456123}

这就是为什么我现在要问,实现BigInteger类的最佳结构和最佳方式是什么?为什么karatsuba算法比长乘法算法慢

这是我的密码:(很抱歉这么长)

使用名称空间std;
bool _ulongmult=真;
bool_uuukaratusuba=假;
结构BigInt{
公众:
矢量数字;
BigInt(常量字符*数字){
//构造函数不相关
}
BigInt(){}
void BigInt::operator=(BigInt a){
数字=a.数字;
}
友元BigInt运算符+(BigInt,BigInt);
友元BigInt算子*(BigInt,BigInt);
friend ostream&运算符a.digits.size(){
a、 digits.swap(b.digits);//确保a的位数大于或等于b
}
整数进位=0;
for(无符号整数i=0;i0);
而(a.digits.size()!=b.digits.size()){//为相等的大小添加零
如果(a.digits.size()>b.digits.size()){
b、 数字。推回(0);
}否则{
a、 数字。推回(0);
}
}
如果(a.digits.size()==1){
长prod=a.位数[0];
产品*=b位数字[0];
res=prod;//从long到BigInt的转换以恒定时间运行
返回res;
}否则{
对于(无符号整数i=0;i
  • 您使用std::vector

    对于您的数字,请确保其中没有不必要的重新分配。因此,请在操作之前分配空间以避免它。此外,我不使用它,因此我不知道数组范围检查的慢化

    检查是否没有移动!!!这是
    O(N)
    …即插入到第一个位置

  • 优化您的实施

    您可以找到我的实现优化了一个未优化的比较

    x=0.98765588997654321000000009876... | 98*32 bits...
    mul1[ 363.472 ms ]... O(N^2) classic multiplication
    mul2[ 349.384 ms ]... O(3*(N^log2(3))) optimized karatsuba multiplication
    mul3[ 9345.127 ms]... O(3*(N^log2(3))) unoptimized karatsuba multiplication 
    
    Karatsuba的矿山实现阈值约为3100位…~944位!!!代码越优化,lover阈值越高


    尝试从函数操作数中删除不必要的数据

    //BigInt operator + (BigInt a,BigInt b)
    BigInt operator + (const BigInt &a,const BigInt &b)
    
    这样,您就不会在每次+调用时在堆上创建另一个
    a,b
    副本,而且更快的是:

    mul(BigInt &ab,const BigInt &a,const BigInt &b) // ab = a*b
    
  • Schönhage-Strassen乘法

    这一个是基于FFTNTT的。我的阈值是大的…~49700位…~15000位,所以如果你不打算使用这么大的数字,那就忘了它。实现也在上面的链接中


    是我的NTT实现(尽可能优化)

  • 摘要

    不管您使用的是小端还是大端,但您应该以不使用插入操作的方式对操作进行编码


    因为需要使用除法和模运算,所以对速度较慢的数字使用十进制基数。如果选择基数作为2的幂,则只需位运算就足够了,而且它还可以从代码中删除许多速度最慢的If语句。如果需要基数作为10的幂,则使用最大的You在某些情况下,这可以将div、mod减少到几个减法吗

    2^32 = 4 294 967 296 ... int = +/- 2147483648
    base = 1 000 000 000
    
    //x%=base
    while (x>=base) x-=base;
    
    在某些平台上,最大循环数为2^32/基或2^31/基是否比模快,并且基越大,所需的操作越少,但请注意溢出


  • 似乎您将<10^9的数字视为一个数字。此IMO将使karatsuba算法更接近原始乘法。尝试将<10的数字视为一个数字进行此操作。此外,您确定进位的最大值最多可以为1吗?这正是我之前所做的。结果:16秒内单次500位乘法nds…我相信使用起来很方便。你可以展示你的整个代码,以便有人可以试用。此外,根据你的系统,你可能会有int溢出。你应该使用探查器来找出时间花在哪里,而不是
    向量数字;
    你建议使用
    向量数字
    ,并将其用作binary表示法?@gelatine1不,您仍然可以使用vector,但基数不是100000000,而是0x100000000,您需要添加的唯一一件事是dec和十六进制字符串之间的转换,如下所示:我更喜欢十六进制表示法,因为它快得多(没有除法,只有bi)
    2^32 = 4 294 967 296 ... int = +/- 2147483648
    base = 1 000 000 000
    
    //x%=base
    while (x>=base) x-=base;