【问题标题】:Repeating Cycle of Modulus模量的重复循环
【发布时间】:2015-02-28 01:46:29
【问题描述】:

有没有找到 1111..nmod M 的值的有效方法?
总是可以使用重复平方来找到
100mod M + 101mod M + 102mod M + 103mod M + ...10nmod M
还有比这更快的方法吗?

【问题讨论】:

  • 欧拉的 Totient 函数为循环的长度设定了一个上限。所以你可以运行它来让你知道首先找到循环是否值得。

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


【解决方案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 代码。如果你想要一个大的M,你必须使用比int更长的类型来避免溢出(你需要M^2来适应你的类型)。

#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;
}

【讨论】:

  • pow_mod_m 中的if (n == 1) { return a; } 不应该是if (n == 1) { return a mod m; }
  • @Bateman 是的,那会更好。我认为除非发生溢出,否则编写的代码不会产生错误的结果,但是使用您的代码版本,产生溢出的值范围更小。
【解决方案2】:

如果 10 是幂零的(如果 M = 2^a * 5^b),则 10 的幂最终会变为 0,或者在某个点开始循环。 (其实0,0,0...也是一个循环。)循环的长度可以大到M-1。因此计算幂直到观察到重复值,并使用简单的代数根据观察到的不同幂来收集项。

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

【讨论】:

  • 有计算周期长度的公式吗?
  • @user3318603 简短回答,并非如此。这是现代密码学所依赖的无法解决的问题之一。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-15
  • 1970-01-01
  • 2017-12-18
  • 1970-01-01
  • 1970-01-01
  • 2014-06-15
  • 2020-10-14
相关资源
最近更新 更多