【问题标题】:Power for big numbers modulo m in CC 中以 m 为模的大数的幂
【发布时间】:2018-08-05 14:43:13
【问题描述】:

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

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*x) % p;

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

【问题讨论】:

  • x*x 仍被评估为 long long,您有一个有符号整数溢出,这会导致未定义的行为。
  • 了解two's complement,这是表示负数的最常用方式。也许使用unsigned 数字看看是否有帮助?如果没有,那么您需要一个 bignum 库。
  • 你可能想要使用一些 bignum 库,例如 GMPlib
  • 所以你需要实现乘以求和
  • 你应该看看 chux Modular exponentiation without range restriction 的这篇文章

标签: c algorithm function long-long


【解决方案1】:

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);

【讨论】:

  • res + a2 * a 仍然可以溢出。这种方法减少了 OF 发生的位置,但并未消除它。
【解决方案2】:

欢迎使用有符号整数溢出和未定义的行为 (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;

【讨论】:

    【解决方案3】:

    除了@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;
    }
    

    【讨论】:

      【解决方案4】:

      如果 Mod == (2^63-1) 则此解决方案无效。

      解决方案:Mod

      (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);
      }
      

      【讨论】:

      • (a%m)+multiply(a,b-1,m)x<<1 仍然可以溢出。这种方法减少了 OF 发生的位置,但并未消除它。
      • (x
      • (a%m)+multiply(a,b-1,m) 这里使用+ 而不是*
      • multiply(a,b-1,m) 返回 %m res
      • (x multiply(LLONG_MAX-1, 2, LLONG_MAX);
      猜你喜欢
      • 2012-01-07
      • 1970-01-01
      • 2012-07-06
      • 2017-04-03
      • 1970-01-01
      • 1970-01-01
      • 2016-01-21
      • 2020-01-09
      相关资源
      最近更新 更多