通过平方模幂运算的溢出可能性

时间:2013-05-07 18:59:29

标签: c++ algorithm primes exponentiation

我希望将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 ++

2 个答案:

答案 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;
}

然后所有p2p3p4的当前值都需要乘以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。请注意,在这种情况下,您使用的数字是允许的最大值的一半。

如果分成两部分是不够的(我怀疑是这种情况),使用相同的模式分成三部分。三分裂应该有效。

一种更简单的方法就是增加学校的方式。它与前一种方法相对应,但是将一个数字写成与数字一样多的部分。

祝你好运!

相关问题