📜  求解范围 [0, N-1] 中 x 值的线性同余 Ax = B (mod N)(1)

📅  最后修改于: 2023-12-03 14:56:00.473000             🧑  作者: Mango

求解范围 [0, N-1] 中 x 值的线性同余 Ax = B (mod N)

线性同余方程(Linear Congruence Equation)是一个形如 Ax ≡ B(mod N) 的数学问题,其中 A、B、N 是已知的整数,求同余方程的解 x,使得 0 ≤ x < N。

可行性
  1. A 与 N 互质: 如果 A 和 N 互质,这个方程就有唯一解。这说的是一个叫 域 理论的数学概念,如果你想了解更多可以自己去了解。
  2. A 与 N 不互质: 如果 A 和 N 不互质,这个方程有无数解。
原理

我们现在要研究 x 在什么情况下有解以及怎么求解。

充要条件
  1. 如果 A 和 N 不互质,那么如果 B 不是 A 和 N 的公因数 B 与 AN 的最大公因数不为 1,所以这里相当于已经做了一个约分。 如果 B 不是 AN 的公因数,那么这个方程无解。否则这个方程有无数解。
  2. 如果 A 和 N 互质,那么在模 N 意义下,A 的乘法同余于 N 个不同的数,也就是我们可以创建 N 个数列: (A * 0) % N, (A * 1) % N, (A * 2) % N, (A * 3) % N, ..., (A * (N-1)) % N。

现在我们找到了一些能够凑出 B 的数(就是同余的意思),假设是 ai(ai 满足 A * ai ≡ B (mod N)),这个方程最终的解将会是如下形式:

x = a1 + k * N,其中 k 是整数

通式可以理解为所有可能解的集合是 a1 + N 的倍数。

如何求解?
  1. 如果有无数个解,怎么选?

我们需要找到所有ai,需要遍历这N个数列。如果所有数列都没有满足 A * ai ≡ B (mod N),返回无解。

  1. 如果只有一个解,怎么求?

    直接使用扩展欧几里得算法,求解 ax + by = gcd(a, n) 的整数解。

  2. 如果已知多个解,怎么选?

    如果我们求出来了一些解,最优解应该是使得 a1 为正且最小的那个值。如果有负的解也需要对 N 取模,逐一判断。

代码实现
Python
def gcd(a, b):
    return a if b == 0 else gcd(b, a % b)
        
def extend_gcd(a, b):
    if b == 0:
        return 1, 0, a
    else:
        x, y, g = extend_gcd(b, a % b)
        return y, x - a // b * y, g

def linear_congruence(a, b, n):
    g = gcd(a, n)

    if b % g != 0:
        return []

    a, b, n = a // g, b // g, n // g
    
    x, y, g = extend_gcd(a, n)

    if g != 1:
        return []

    return [(x * (b % n) % n + n) % n + i * n for i in range(g)]

print(linear_congruence(3, 2, 7))  # [5]
print(linear_congruence(6, 4, 20))  # [2, 7, 12, 17]
C++
#include <iostream>
#include <vector>

using namespace std;

int gcd(int a, int b) {
    return b == 0 ? a : gcd(b, a % b);   // Euclidean Algorithm
}

int extend_gcd(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1, y = 0;
        return a;
    } else {
        int g = extend_gcd(b, a % b, y, x);
        y -= a / b * x;
        return g;
    }
}

vector<int> linear_congruence(int a, int b, int n) {
    vector<int> res;
    int x, y, g;
    g = gcd(a, n);

    if (b % g != 0) {
        return res;
    }

    a /= g, b /= g, n /= g;
    extend_gcd(a, n, x, y);

    for (int i = 0; i < g; i++) {
        res.push_back((x * (b % n) % n + n) % n + i * n);
    }

    return res;
}

int main() {
    vector<int> res = linear_congruence(3, 2, 7);
    for (int i = 0; i < res.size(); i++) {
        cout << res[i] << " ";
    }
    cout << endl;

    res = linear_congruence(6, 4, 20);
    for (int i = 0; i < res.size(); i++) {
        cout << res[i] << " ";
    }
    cout << endl;

    return 0;
}

以上代码实现只适用于解有限解的线性同余方程。对于解集很大、无限的情况,我们还可以使用 Baby-Step Giant-Step、Pollard Rho 等算法进行优化。