中国剩余定理(解决模数互质的同余方程组)
在《孙子算经》中有这样一个问题:“今有物不知其数,三三数之剩二(除以3余2),五五数之剩三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题的一般解法国际上称为“中国剩余定理”。
具体解法分三步:
- 找到三个数:从3和5的公倍数中找出被7除余2的最小数30,从3和7的公倍数中找出被5除余3 的最小数63,最后从5和7的公倍数中找出除3余2的最小数140。
- 然后把这三个数相加得到30 + 63 + 140 = 233满足以上所有条件。
- 233满足以上所有条件,但是并不是最小的解, 用233除以3,5,7三个数的最小公倍数105,得到余数23,即233%105=23。这个余数23就是符合条件的最小数。
问题:
- 第一步为什么要在其他两个数的公倍数中寻找满足条件的数?
- 第一步中怎么寻找这三个满足条件的数?
- 第二步为什么相加就能满足上述条件?
- 第三步为什么可以对他们的最小公倍数取模?
- 为什么只能解决模数互质的同余方程组?
解答:
- 我们假设 x 满足模3余2(如2,5,8等),那么 x + 3 * k 也满足模三余2。所以对于30,63,140这三个数,140(x) + 63(3*k) = 203 满足模3余2,并且满足模5余3。同理30 + 63 + 140 = 233 满足:模3余2且模5余3模7余2。在其他两个数的公倍数中寻找满足条件的数是为了相加时对各自的余数不造成影响。
- 用扩展欧几里得求解,比如求3和5的公倍数中被7除余2的最小数,设该数为15 * X,则 15 * X = 2 (mod 7),可列出方程:15 * X + 7 * Y = 2 ,通过扩展欧几里得可求出最小的正整数解。
- 见解答1。
- 由解答1可知,对最小公倍数去模不会影响结果,取模以的到最小的解。
- ??求大佬解释
模板:
LL CRT(LL m[], LL a[], int n){ // ans % m = a
LL M = 1, ans = 0;
for(int i=0; i<n; i++) M *= m[i];
for(int i=0; i<n; i++){
LL x, y;
LL Mi = M / m[i];
EX_gcd(Mi, m[i], x, y);
ans = (ans + mod_mul(mod_mul(Mi, x, M), a[i], M)) % M;
}
if(ans < 0) ans += M;
return ans;
}
扩展中国剩余定理(解决模数可不互质的同余方程组)
普通的中国剩余定理要求所有的互素,那么如果不互素呢,怎么求解同余方程组?
这种情况就采用两两合并的思想,假设要合并如下两个方程:
那么得到:
我们需要求出一个最小的 x 使它满足:
那么x1和x2就要尽可能的小,于是我们用扩展欧几里得算法对 求出x1的最小正整数解,将它代回,得到x的一个特解x′,当然也是最小正整数解。
所以x的通解一定是x′加上,这样才能保证x模m1和m2的余数是a1和a2。由此,我们把这个x′当做新的方程的余数,把当做新的方程的模数(关键)。
合并完成:
每次两两合并直到把n和方程合并成一个,最终得到解。
优点 :
- 解决了不互质问题。
- 可判断是否有解。
- 计算过程为中提高了精度。
模板:
LL EX_CRT(LL m[], LL a[], int n) { // ans % m = a
LL M = m[0], ans = a[0];
for(int i=1; i<n; i++)
{
LL aa = M, bb = m[i], cc = (a[i] - ans % bb + bb) % bb;
LL x = 0, y = 0;
LL d = EX_gcd(aa, bb, x, y);
bb = bb / d;
if(cc % d) return -1; // 不存在
x=((x * cc / d) % bb + bb) % bb;
ans += M * x; M *= bb;
ans = (ans % M + M) % M;
}
return ans;
}