对于C ++中的大型mod,模幂运算失败

时间:2015-10-22 05:33:38

标签: c++ algorithm cryptography exponentiation modular-arithmetic

这是我用来计算(n^p)%mod的代码。不幸的是,当我从mod方法调用它时,mod = 10000000000ULL(在我的情况下为main())的值很大。任何的想法;为什么呢?

ull powMod(ull n, ull p, ull mod) {
    ull ans = 1;
    n = n%mod;
    while(p) {
        if(p%2 == 1) {
            ans = (ans*n)%mod;
        }
        n = (n*n)%mod;
        p /= 2;
    }
    return ans;
}

此处,ullunsigned long long的typedef。

4 个答案:

答案 0 :(得分:3)

是的,你可以用C ++来做。正如其他人指出你不能直接那样做。使用一点数论可以将问题分解为两个可管理的子问题。

首先考虑10^10 = 2^10 * 5^10。这两个因素都是互质的,因此您可以使用{modoro 10^10和modulo 2^10来为Chinese remainder theorem找到幂模5^10

请注意,在以下代码中,使用Extended Euclidean Algorithm找到了 magic u2u5。您不需要自己编写此算法,因为这些值是常量。我使用maxima及其gcdex函数来计算它们。

以下是修改后的版本:

typedef unsigned long long ull;

ull const M  = 10000000000ull;

ull pow_mod10_10(ull n, ull p) {
  ull const m2 = 1024;    // 2^10
  ull const m5 = 9765625; // 5^10
  ull const M2 = 9765625; // 5^10 = M / m2
  ull const M5 = 1024;    // 2^10 = M / m5
  ull const u2 = 841;     // u2*M2 = 1 mod m2
  ull const u5 = 1745224; // u5*M5 = 1 mod m5

  ull b2 = 1;
  ull b5 = 1;
  ull n2 = n % m2;
  ull n5 = n % m5;

  while(p) {
    if(p%2 == 1) {
      b2 = (b2*n2)%m2;
      b5 = (b5*n5)%m5;
    }
    n2 = (n2*n2)%m2;
    n5 = (n5*n5)%m5;
    p /= 2;
  }

  ull np = (((b2*u2)%M)*M2)%M;
  np    += (((b5*u5)%M)*M5)%M;
  np    %= M;
  return np;
}

答案 1 :(得分:1)

似乎你无法避免它。

如果mod10000000000ULL,则在您的计划的(a*b)%c中,ab都小于mod,因此我们将其视为{{1} },9999999999ULL将为a*b,但99999999980000000001只能表达unsigned long long,因此您的方法会溢出。

答案 2 :(得分:0)

这里可能出现的问题之一似乎是当你(a*b)%c时,a*b部分本身可能会溢出,从而导致错误答案。解决这个问题的一种方法是使用

标识
(a*b)%c 

相当于

(a%c * b%c)%c

这也可以防止中间乘法中的溢出。

答案 3 :(得分:0)

您的代码行

 n = (n*n)%mod;

重复执行。 只要n小于mod,这可能会导致在某个时间点评估(mod-1)*(mod-1)。

输入n可能不是那么大,但是上面提到的代码行在循环中增加了n。