火龙信奥
  • 首页
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

中国剩余定理 (Chinese Remainder Theorem)

作者: 作者的头像   huolong , 时间:2026-08-06 13:12:54 , 所有人可见, 阅读  3

算法讲义:中国剩余定理 (Chinese Remainder Theorem)

1. 经典中国剩余定理 (孙子定理)

1.1 问题引入

《孙子算经》中著名的“物不知其数”问题:

“今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?”

转换成现代数学语言,即求解同余方程组: $$ \begin{cases} x \equiv a_1 \pmod{m_1} \ x \equiv a_2 \pmod{m_2} \ \dots \ x \equiv a_n \pmod{m_n} \end{cases} $$ 在经典 CRT 中,要求 $m_1, m_2, \dots, m_n$ 两两互质。

1.2 构造法求解步骤

  1. 计算所有模数的乘积 $M = \prod_{i=1}^n m_i$。
  2. 对于每个方程 $i$:
    • 计算 $M_i = M / m_i$(即除 $m_i$ 外其余模数的乘积)。
    • 计算 $M_i$ 模 $m_i$ 的逆元 $t_i$,即满足 $M_i t_i \equiv 1 \pmod{m_i}$。
  3. 方程组的一个特解为:$x_0 = \sum_{i=1}^n a_i M_i t_i$。
  4. 通解为:$x = x_0 + kM$。最小正整数解为 $x_0 \bmod M$。

1.3 代码实现 (C++)

typedef long long ll;

// 扩展欧几里得算法求逆元
void exgcd(ll a, ll b, ll &x, ll &y) {
    if (b == 0) { x = 1, y = 0; return; }
    exgcd(b, a % b, y, x);
    y -= a / b * x;
}

ll crt(int n, ll a[], ll m[]) {
    ll M = 1, ret = 0;
    for (int i = 0; i < n; i++) M *= m[i];
    for (int i = 0; i < n; i++) {
        ll Mi = M / m[i];
        ll x, y;
        exgcd(Mi, m[i], x, y); // 求 Mi 在模 mi 下的逆元 x
        x = (x % m[i] + m[i]) % m[i]; // 保证 x 为正
        ret = (ret + a[i] * Mi % M * x % M) % M;
    }
    return (ret + M) % M;
}

复杂度:$O(n \log m)$,其中 $m$ 是模数的大小。


2. 扩展中国剩余定理 (EXCRT)

2.1 为什么要扩展?

当模数 $m_i, m_j$ 不互质时,经典 CRT 失效(因为 $M_i$ 模 $m_i$ 的逆元可能不存在)。此时需要使用两两合并的思想。

2.2 方程合并推导

假设已有两个方程: 1. $x = k_1 m_1 + a_1$ 2. $x = k_2 m_2 + a_2$

联立得: $$k_1 m_1 + a_1 = k_2 m_2 + a_2 \implies k_1 m_1 - k_2 m_2 = a_2 - a_1$$

这是一个关于 $k_1, k_2$ 的二元一次不定方程。根据裴蜀定理: - 无解判定:若 $(a_2 - a_1) \bmod \gcd(m_1, m_2) \neq 0$,则方程组无解。 - 求解 $k_1$:利用 exgcd 求出 $k_1 m_1 + k_2 (-m_2) = \gcd(m_1, m_2)$ 的一个特解 $k_{1}'$,然后放大 $\frac{a_2-a_1}{\gcd(m_1, m_2)}$ 倍。

得到 $k_1$ 后,代入 $x = k_1 m_1 + a_1$,可得新的通解形式: $$x \equiv X_{new} \pmod{\text{lcm}(m_1, m_2)}$$ 其中 $X_{new} = k_1 m_1 + a_1$。

2.3 算法步骤

  1. 初始化第一个方程:$M = m_1, Ans = a_1$。
  2. 逐个合并后续方程 $(a_i, m_i)$:
  3. 求解 $k \cdot M \equiv a_i - Ans \pmod{m_i}$。
  4. 使用 exgcd 求出 $k$。
  5. 更新 $Ans = Ans + k \cdot M$。
  6. 更新 $M = \text{lcm}(M, m_i)$。
  7. 循环结束后的 $Ans \bmod M$ 即为答案。

2.4 代码实现 (C++)

ll excrt(int n, ll a[], ll m[]) {
    ll m1 = m[0], a1 = a[0];
    for (int i = 1; i < n; i++) {
        ll m2 = m[i], a2 = a[i];
        ll x, y;
        ll g = __gcd(m1, m2);
        if ((a2 - a1) % g != 0) return -1; // 无解

        exgcd(m1 / g, m2 / g, x, y); // 求解关键步骤

        // 计算当前 k1 的特解,注意防止溢出
        ll mod = m2 / g;
        x = (__int128)x * ((a2 - a1) / g) % mod;
        if (x < 0) x += mod;

        // 合并方程
        a1 = a1 + x * m1;
        m1 = m1 / g * m2; // 新的模数是 lcm(m1, m2)
        a1 = (a1 % m1 + m1) % m1;
    }
    return a1;
}

3. CRT 与 EXCRT 总结对比

特性 经典 CRT 扩展 EXCRT
模数要求 必须两两互质 无限制
核心思想 构造法 方程两两合并
工具 逆元 (Inverse) 扩欧 (exgcd)
适用场景 模数较小且为质数乘积时 模数任意,通用性更强

应用场景

  1. 大数取模:当运算结果超过 long long 但模数是几个小质数的乘积。
  2. Lucas 定理扩展:当卢卡斯定理中的模数 $p$ 不是质数时,需分解质因数后用 CRT 合并。
  3. 密码学:RSA 算法中的加速运算。

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

©2026加盟我们 | 关于我们 | ACM课程 | 常见问题 | 成果墙 | 评测记录 | 浙ICP备2021013995号
在线画图 | OI WIki | 打字练习
火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码