中国剩余定理及其扩展

中国剩余定理

假如给定一组同余方程:

\[\begin{cases} x \equiv a_1 \text{mod} m_1 \\ x \equiv a_2 \text{mod} m_2 \\ \vdots \\ x \equiv a_k \text{mod} m_k \end{cases}\]

其中 \(m_1, m_2, \ldots, m_k\) 两两互质,下面介绍如何求解该组方程。

我们令 \(M = m_1 \cdot m_2 \cdots m_k\),并且对于每个 \(i\),定义 \(M_i = \frac{M}{m_i}\)。 由于 \(m_i\) 和 \(M_i\) 互质,可以知道 \(M_i\) 在模 \(m_i\) 意义下是有逆元的, 记为 \(M_i^{-1}\)。不难发现 \(M_i \cdot M_i^{-1} \equiv 1 \text{mod} m_i\) 和 \(M_i \cdot M_i^{-1} \equiv 0 \text{mod} m_j\)(\(j \neq i\))都成立。 记 \(c_i = a_i \cdot M_i \cdot M_i^{-1}\),则有:

\[\begin{cases} c_i \equiv a_i \text{mod} m_i \\ n_i \equiv 0 \text{mod} m_j \quad (j \neq i) \end{cases}\]

由同余的线性性质可知,\(x' = \sum_{i=1}^{k} c_i\) 即为一个特解。

不难证明通解可以写成 \(x = x' + t \cdot M \quad (t \in \mathbb{Z})\)。

注意在上述的过程中,我们要求解 \(M_i\) 在模 \(m_i\) 意义下的逆元, 只有在 \(m_i\) 两两互质的情况下才能保证每次求解的逆元存在。

代码如下:

template <typename T>
static void crt(const std::vector<T> &a, const std::vector<T> &m, T &x, T &l) {
    assert(a.size() == m.size());
    x = 0;
    l = 1;
    for (auto &&e : m) { l *= e; }
    for (int i = 0; i < a.size(); i++) {
        x = (x + a[i] * (l / m[i]) % l * inverse_of(l / m[i], m[i]) % l) % l;
    }
}

扩展中国剩余定理

扩展中国剩余定理是在中国剩余定理的基础上,放宽了对模数的互质要求。 假设给定一组同余方程:

\[\begin{cases} x \equiv a_1 \text{mod} m_1 \\ x \equiv a_2 \text{mod} m_2 \\ \vdots \\ x \equiv a_k \text{mod} m_k \end{cases}\]

并不保证 \(m_1, m_2, \ldots, m_k\) 两两互质,下面介绍如何求解该组方程。

不难发现,\(x = x' + t \cdot M \quad (t \in \mathbb{Z})\) 是同余方程 \(x \equiv x' \text{mod}{M}\) 的解。

假设我们已经求出了前 \(i-1\) 个方程的解:

\[x = x_{i-1} + t \cdot M_{i-1} \quad (t \in \mathbb{Z}),\]

现在考虑如何求其与

\[x \equiv a_i \text{mod}{m_i}\]

的公共解。

将当前的解代入上述方程,得到:

\[x_{i-1} + t \cdot M_{i-1} \equiv a_i \text{mod}{m_i},\]

\[t \cdot M_{i-1} \equiv a_i - x_{i-1} \text{mod}{m_i}.\]

设 \(d = \gcd(M_{i-1}, m_i)\),则上式有解的充要条件是\(d \mid (a_i - x_{i-1})\)。

如果有解,将上式两边同时除以 \(d\),得到:

\[t \cdot \frac{M_{i-1}}{d} \equiv \frac{a_i - x_{i-1}}{d} \text{mod}{\frac{m_i}{d}}.\]

由于\(\frac{M_{i-1}}{d}\)和\(\frac{m_i}{d}\)互质, 可以用扩展欧几里得算法求该方程的一个特解 \(t_0\),则通解可以表示为:

\[t = t_0 + k \cdot \frac{m_i}{d} \quad (k \in \mathbb{Z}).\]

将通解代入\(x = x_{i-1} + t \cdot M_{i-1}\)中,可以得到:

\[x = x_{i-1} + t_0 \cdot M_{i-1} + k \cdot \frac{m_i}{d} \cdot M_{i-1} \quad (k \in \mathbb{Z}).\]

因此,新的解可以表示为:

\[x = x_i + k \cdot M_i \quad (k \in \mathbb{Z}),\]

其中

\[\begin{cases} x_i = x_{i-1} + t_0 \cdot M_{i-1} \\ M_i = \frac{m_i}{d} \cdot M_{i-1} \end{cases}\]

特别地,我们可以增加一个方程 \(x \equiv 0 \text{mod} 1\) 作为初始条件, 此时 \(x_0 = 0\),\(M_0 = 1\)。这里给出代码:

template <typename T>
static void ex_crt(const std::vector<T> &a, const std::vector<T> &m, T &x, T &l) {
    x = 0;
    l = 1;
    for (int i = 0; i < a.size(); i++) {
        T t0, _;
        T d = gcd(l, m[i]);
        if ((a[i] - x) % d != 0) {
            x = l = -1;
            return;
        }
        ex_gcd(l / d, m[i] / d, t0, _);
        t0 = (a[i] - x) / d * t0 % (m[i] / d);
        t0 = (t0 % (m[i] / d) + (m[i] / d)) % (m[i] / d);
        x = x + t0 * l;
        l = m[i] / d * l;
        x %= l;
    }
}



    Enjoy Reading This Article?

    Here are some more articles you might like to read next:

  • 模逆元
  • 扩展欧几里德算法
  • Miller Rabin 素数测试
  • Cradle to Grave Devotion: Objective C Notes
  • gcs-front-end Development