模数的重复循环

时间:2015-02-28 01:46:30

标签: c++ algorithm discrete-mathematics modular-arithmetic

是否有一种有效的方法可以找到1111 .. n mod M的值? 人们总是可以使用重复的平方来找到10 0 mod M + 10 1 mod M + 10 2 mod M + 10 < sup> 3 mod M + ... 10 n mod M
有没有比这更快的方法?

2 个答案:

答案 0 :(得分:1)

你可以使用算法解决这个问题,几乎就像通过平方取幂一样。

首先,如果您的偶数为1,您可以看到:

11111111 = 1111     * 10001
n ones     n/2 ones   (10^(n/2) + 1)

将一个数量加倍。此外,

1111111 = 111111   * 10 + 1
n ones    n-1 ones

将这些观察结果正式化,为了方便起见,将n为111 ... 1的数字命名为J(n):

  • 如果n是偶数,则J(n)=(10 ^(n / 2)+ 1)J(n / 2)。
  • 如果n是奇数,则J(n)= 10 * J(n-1)+ 1。

你可以使用这些重现(加上实数的取幂,通过平方计算10 ^(n / 2))模M来计算O(log(n)^ 2)时间内的结果。

这是实现此功能的一些C代码。如果你想要一个大的int(你需要M ^ 2适合你的类型),你必须使用比M更长的类型来避免溢出。

#include <stdio.h>

// Computes a to the power of n modulo m.
int pow_mod_m(int a, int n, int m) {
    if (n == 0) { return 1; }
    if (n == 1) { return a; }
    if (n % 2 == 0) {
        int k = pow_mod_m(a, n/2, m);
        return (k * k) % m;
    }
    return (pow_mod_m(a, n-1, m) * a) % m;
}

// Computes J(n) modulo m
int j_mod_m(int n, int m) {
    if (n == 1) {
        return 1;
    }
    if (n % 2 == 0) {
        return (j_mod_m(n/2, m) * (1 + pow_mod_m(10, n/2, m))) % m;
    }
    return (j_mod_m(n-1, m) * 10 + 1) % m;
}

int main(int argc, char**argv) {
    for (int i = 1; i < 1000; i++) {
        printf("%d: %d\n", i, j_mod_m(i, 12345));
    }
    return 0;
}

答案 1 :(得分:0)

如果10是幂零的(如果M = 2 ^ a * 5 ^ b),或者在某个时刻开始循环,那么10的幂将最终达到0。 (实际上0,0,0 ......也是一个循环。)循环的长度可以与M-1一样大。因此,计算功率直到观察到重复值,并使用简单代数根据观察到的不同功率来收集术语。

我认为这会将复杂性从O(n)降低到O(M),这显然是对大n的改进。