算法学习笔记(38)——中国剩余定理
中国剩余定理
设 是两两互质的整数, ,(除了 之外其他所有 的乘积), 是线性同余方程 的一个解,即 是 模 的乘法逆元。对于任意的 个整数 ,方程组
有整数解,解为 。
证明:
因为 是除 之外所有模数的倍数,所以 。
又因为 ,所以代入 ,原方程组成立。
证毕
中国剩余定理给出了模数两两互质的线性同余方程组的一个特解。方程组的通解可以表示为 。有些题目要求我们求出最小的非负整数解,只需把 对 取模,并让 落在 ~ 的范围内即可。
另外,及时模数不满足两两互质,我们也有方法判断线性同余方程是否有解,并求出方程组的解。
本题中 不一定两两互质,中国剩余定理不再适用。可以考虑使用数学归纳法,假设已经求出了前 个方程构成的方程组的一个解 。记 ,则 是前 个方程的通解。
以前两个方程举例说明:
则
于是问题转化为,在已知 的前提下,求一组 满足以上线性同余方程,而线性同余方程有解的充要条件是 。
根据扩展欧几里得算法,我们可以先求出线性同余方程 的一组特解 ,再将方程两边同时乘以 ,即可得到方程 的一组特解:
该方程的通解可以表示为:
记 ,于是
记 ,,则我们将方程组内前两个方程合为一个方程:
对后续方程递归进行上述操作,即可将方程组合并为一个方程,最后得出:
#include <iostream>
using namespace std;
typedef long long LL;
// 扩展欧几里得算法,返回a与b的最大公约数,同时求出ax+by=gcd(a,b)的解(x,y)
LL exgcd(LL a, LL b, LL &x, LL &y)
{
if (!b) { x = 1, y = 0; return a; }
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main()
{
int n;
cin >> n;
bool has_answer = true; // 代表是否有解
LL a1, m1; // 每次将一组新的方程合并入现有方程
cin >> a1 >> m1; //读入第一个方程
// 读入之后的n-1个方程
for (int i = 0; i < n - 1; i ++ ) {
LL a2, m2;
cin >> a2 >> m2;
LL k1, k2;
LL d = exgcd(a1, a2, k1, k2);
// 若不满足线性同余方程有解的充要条件 => gcd(a1,a2) | m2 - m1,则跳出循环,置为false
if ((m2 - m1) % d) {
has_answer = false;
break;
}
// 转换特解为通解 k1a1 - k2a2 = gcd(a1,a2) => k1a1 - k2a2 = m2 - m1
k1 *= (m2 - m1) / d;
LL t = a2 / d;
k1 = (k1 % t + t) % t; // 在转换过程中求出最小的非负整数解,防止溢出
// 更新合并后的a1 与 m1
m1 = a1 * k1 + m1;
a1 = abs(a1 / d * a2);
}
// C++ 取模可能得到负数,所以对取模的结果加m1再模m1,即可转为正数
if (has_answer) cout << (m1 % a1 + a1) % a1 << endl;
else puts("-1");
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】