我希望将fermat's little theorem用于主要测试。这是我写的代码:
lld expo(lld n, lld p) //2^p mod n
{
if(p==0)
return 1;
lld exp=expo(n,p/2);
if(p%2==0)
return (exp*exp)%n;
else
return (((exp*exp)%n)*2)%n;
}
bool ifPseudoPrime(lld n)
{
if(expo(n,n)==2)
return true;
else
return false;
}
注意:我将a(<=n-1)
的值设为2
。
现在,数字n可以与10^18
一样大。这意味着变量exp
可以达到10^18
附近的值。这进一步意味着表达式(exp*exp)
可以达到10^36
那么高,从而导致溢出。我该如何避免这种情况。
我对此进行了测试,直到10^9
才运行良好。我正在使用 C ++
答案 0 :(得分:8)
如果模数接近您可以使用的最大整数类型的限制,则事情变得有些复杂。如果您不能使用实现大整数算术的库,您可以通过在低阶和高阶部分中分割因子来自行滚动模乘。
如果模数m
太大而2*(m-1)
溢出,事情变得非常挑剔,但如果2*(m-1)
没有溢出,那就太可以了。
假设您拥有并使用64位无符号整数类型。
您可以通过将因子分为低和高32位来计算模块化产品,然后产品分成
a = a1 + (a2 << 32) // 0 <= a1, a2 < (1 << 32)
b = b1 + (b2 << 32) // 0 <= b1, b2 < (1 << 32)
a*b = a1*b1 + (a1*b2 << 32) + (a2*b1 << 32) + (a2*b2 << 64)
要使用a*b (mod m)
计算m <= (1 << 63)
,请将m
模拟的四种产品中的每一种减少,
p1 = (a1*b1) % m;
p2 = (a1*b2) % m;
p3 = (a2*b1) % m;
p4 = (a2*b2) % m;
和合并轮班的最简单方法是
for(i = 0; i < 32; ++i) {
p2 *= 2;
if (p2 >= m) p2 -= m;
}
p3
的相同内容以及p4
的64次迭代。然后
s = p1+p2;
if (s >= m) s -= m;
s += p3;
if (s >= m) s -= m;
s += p4;
if (s >= m) s -= m;
return s;
这种方式不是很快,但是对于这里需要的几次乘法,它可能足够快。应通过减少班次数来获得小幅加速;首先计算(p4 << 32) % m
,
for(i = 0; i < 32; ++i) {
p4 *= 2;
if (p4 >= m) p4 -= m;
}
然后所有p2
,p3
和p4
的当前值都需要乘以2 32 modulo m
,
p4 += p3;
if (p4 >= m) p4 -= m;
p4 += p2;
if (p4 >= m) p4 -= m;
for(i = 0; i < 32; ++i) {
p4 *= 2;
if (p4 >= m) p4 -= m;
}
s = p4+p1;
if (s >= m) s -= m;
return s;
答案 1 :(得分:0)
您可以分几个阶段进行乘法运算。例如,假设您要计算X * Y mod n。取X和Y并将它们写为X = 10 ^ 9 * X_1 + X_0,Y = 10 ^ 9 * Y_1 + Y_0。然后计算所有四个乘积X_i * Y_j mod n,最后计算X = 10 ^ 18 *(X_1 * Y_1 mod n)+ 10 ^ 9 *(X_0 * Y_1 + X_1 * Y_0 mod n)+ X_0 * Y_0。请注意,在这种情况下,您使用的数字是允许的最大值的一半。
如果分成两部分是不够的(我怀疑是这种情况),使用相同的模式分成三部分。三分裂应该有效。
一种更简单的方法就是增加学校的方式。它与前一种方法相对应,但是将一个数字写成与数字一样多的部分。
祝你好运!