计算以数字为模的数字的幂的总和

时间:2015-06-03 08:35:42

标签: c++ algorithm

有3个数字:T, N, M. 1 ≤ T, M ≤ 10^9, 1 ≤ N ≤ 10^18

问题中的问题是计算[Σ(T^i)]mod(m),其中i从0变化到n。显然,由于1秒的时间限制,O(N)或O(M)解决方案不起作用。我该怎么办?

4 个答案:

答案 0 :(得分:1)

正如之前的答案中所指出的,您可以使用几何级数和的公式。然而,存在一个小问题 - 如果m不是素数,则计算(T^n - 1) / (T - 1)不能直接完成 - 除法不是明确定义的操作。事实上,有一种解决方案甚至可以处理非主要模块,并且具有复杂性O(log(n) * log(n))。该方法类似于二进制求幂。这是我用c ++编写的代码(注意我的解决方案在内部使用二进制取幂):

typedef long long ll;
ll binary_exponent(ll x, ll y, ll mod) {
  ll res = 1; 
  ll p = x; 
  while (y) {
    if (y % 2) { 
      res = (res * p) % mod; 
    }    
    p = (p * p) % mod; 
    y /= 2;
  }
  return res; 
}

ll gp_sum(ll a, int n, ll mod) {
  ll A = 1; 
  int num = 0; 
  ll res = 0; 
  ll degree = 1; 
  while (n) {
    if (n & (1 << num)) {
      n &= (~(1 << num));
      res = (res + (A * binary_exponent(a, n, mod)) % mod) % mod; 
    }    
    A = (A + (A * binary_exponent(a, degree, mod)) % mod) % mod; 
    degree *= 2;
    num++;
  }
  return res; 
}

在此解决方案A中连续存储值11 + a1 + a + a^2 + a^3,... 1 + a + a^2 + ... a ^ (2^n - 1)

就像二进制求幂一样,如果我想计算n a度的总和,我将n分成两个幂的和(基本上使用二进制表示法) n)。现在具有A的上述值序列,我选择适当的长度(对应于1的二进制表示的n位的长度)并将该和乘以某个值a将结果累加到res。计算A的值将花费O(log(n))时间,对于每个值,我可能需要计算a度,这将导致另一个O(log(n)) - 因此我们总体而言{ {1}}。

我们举一个例子 - 我们想要计算O(log(n) * log (n))。在这种情况下,我们致电1 + a + a^2 .... + a ^ 10

在第一次迭代时,gp_sum(a, 11, mod)不为零,因为11的第一位(n & (1 << 0))为1.因此,我将此位设置n关闭为1011(2),并在res中累积:10 = 0 + 1 * (a ^ (10))。 A现在是a^10

下一个第二位也设置为10(a + 1),因此现在n变为1010(2)8变为res = a^10 + (a + 1)*(a^8)。 A现在是a^10 + a^9 + a^8

下一位为0,因此res保持不变,但A将变为1 + a + a^2 + a^3

在最后一次迭代中,该位为1,因此我们有: 1 + a + a^2 + ... a^7 = res = a^10 + a^9 + a^8 + a^0 *(1 + a + a^2 + ... +a^7)

答案 1 :(得分:0)

可以使用类似于二进制求幂的算法:

// Returns a pair <t^n mod m, sum of t^0..t^n mod m>,
// I assume that int is big enough to hold all values without overflowing. 
pair<int, int> calc(int t, int n, int m) 
    if n == 0 // Base case. t^0 is always 1.
        return (1 % m, 1 % m)
    if n % 2 == 1
        // We just compute the result for n - 1 and then add t^n.
        (prevPow, prevSum) = calc(t, n - 1, m)
        curPow = prevPow * t % m
        curSum = (prevSum + curPow) % m
        return (curPow, curSum)
    // If n is even, we compute the sum for the first half.
    (halfPow, halfSum) = calc(t, n / 2, m)
    curPow = halfPow * halfPow % m // t^n = (t^(n/2))^2
    curSum = (halfSum * halfPow + halfSum) % m 
    return (curPow, curSum)

时间复杂度为O(log n)(分析与二进制求幂算法相同)。为什么它比几何级数的封闭式公式更好?后者涉及(t - 1)除以。但不保证存在t - 1 mod m的倒数。

答案 2 :(得分:-1)

你可以用这个:

a^1 + a^2 + ... + a^n = a(1-a^n) / (1-a)

所以,你只需要计算:

a  * (1 - a^n) / (1 - a) mod M

你可以找到O(logN)方法来计算一个^ n mod M

答案 3 :(得分:-2)

它是一个几何级数,其总和等于:

enter image description here