以C为模的大数的幂

时间:2018-08-05 14:43:14

标签: c algorithm function long-long

我正在使用以下函数来计算以m为模的大数的幂,其中m是任何整数,即(a ^ b)%m

Localizable.strings

但是,对于某些数字,即使此功能也不起作用。例如,如果我打电话

long long power(long long x, long long y, long long p)
{
    long long res = 1;      // Initialize result

    x = x % p;  // Update x if it is more than or
                // equal to p

    while (y > 0)
    {
        // If y is odd, multiply x with result
        if (y & 1)
            res = (res*x) % p;

        // y must be even now
        y = y>>1; // y = y/2
        x = (x*x) % p;
    }
    return res;
}

我得到一个负数作为输出。发生这种情况是由于以下原因: 幂函数中有一行:

 power(1000000000000,9897,52718071807);

当x大时,假设x = 46175307575,执行x =(x * x)%p后存储在x中的值将变为负数。我不明白为什么会这样。即使(x * x)的值超过了long long int的上限,我也不会将其值存储在任何地方,而只是存储(x * x)%p,其值应介于0到p之间。另外,由于p不会跨越长距离,x会如何穿越它?请告诉我为什么会出现此问题以及如何解决此问题。

4 个答案:

答案 0 :(得分:3)

此功能在GeeksForGeeks

// Returns (a * b) % mod
long long moduloMultiplication(long long a,
                               long long b,
                               long long mod)
{
    long long res = 0;  // Initialize result

    // Update a if it is more than
    // or equal to mod
    a %= mod;

    while (b)
    {
        // If b is odd, add a with result
        if (b & 1)
            res = (res + a) % mod;

        // Here we assume that doing 2*a
        // doesn't cause overflow
        a = (2 * a) % mod;

        b >>= 1;  // b = b / 2
    }

    return res;
}

使用它代替

x = (x*x) % p;

x = moduloMultiplication(x, x, p);

而不是

res = (res*x) % p

res = moduloMultiplication(res, x, p);

答案 1 :(得分:1)

欢迎使用有符号整数溢出和未定义行为(UB)。

  

我只是存储(x * x)%p,其值应介于0到p之间。

这是不正确的。 x*x可能溢出long long数学,结果为UB。 @Osiris。样本UB包含一个负数且其操作数为正的乘积。

some_negative_value % some_positive_p产生值。 -see ref。这超出了[0...p)范围。

解决方案是不溢出有符号整数数学。


一个简单的第一步是使用无符号整数数学。

Modular exponentiation without range restriction

这里是没有溢出问题的全方位解决方案

请注意,OP的代码也不会遇到特殊情况:power(some_x, 0, 1),因为当预期为0时它将返回1。

// Fix
// long long res = 1;
long long res = 1%p;
// or 
long long res = p != 1;

答案 2 :(得分:0)

如果Mod ==(2 ^ 63-1),则此解决方案将不起作用。

解决方案:Mod <= 2 ^ 62

(p-1)*(p-1)> 2 ^ 63。所以会有溢出。您需要使用模实现乘法。

尝试一下:

long long multiply(long long a,long long b,long long m){
if(b == 0){
    return 0;
}
if(b==1){
    return a%m;
}
if(b&1){
    return ((a%m)+multiply(a,b-1,m))%m;
}
long long x = multiply(a,b>>1,m);
return multiply(x,2,m);
}

long long bigmod(long long a,long long b, long long m){
if(b == 0){
    return 1;
}
if(b == 1){
    return a%m;
}
if(b & 1){
    return multiply(a%m,bigmod(a,b-1,m),m);
}
long long x = bigmod(a,b>>1,m);
return multiply(x,x,m);
}

答案 3 :(得分:0)

除了@Doug Currie提到的解决方案之外,您还可以使用128位数据类型__int128

long long pow(long long a, long long b, long long mod)
{
    __int128 res = 1;
    while(b > 0)
    {
        if(b&1)
        {
            res = (res*a);
            res = res%mod;
        }
        b = b>>1;
        a = ((__int128)a*a)%mod;
    }
    return res;
}