求同余方程组(中国剩余定理)
在《孙子兵法》中有一这样问题:“今有物不知其数,三三数之剩二(除以3余2),五五剩值三(除以5余3),七七数之剩二(除以7余2),问物几何?”这个问题称为“孙子问题”,该问题解法称为中国剩余定理。具体分三步:
5%3 = 2
8%5 = 3
9%7 = 2
发现 5 8 9 这三个是满足上面的要求,但是并不满足x为一个数的要求。
一、这里我们先考虑当是两个数同余的情况:
怎么办呢,就考虑着能不能让这两个数统一起来呢,我就让 5 先变大一点,让这个数既可以%3 = 2 ,又可以 %5 = 3。
那么就列两个式子
x1 : 3k1 + 2 = 2(mod 3) (1)
x2 : 5k2 + 3 = 3(mod 5) (2)
所以这个时候是不是这要满足上式,就会一直成立。
但是必须要考虑个问题,怎么让这两个式子合并呢?
合并后可以(mod 3) = 2,又可以(mod 5)= 3 。
那是不是就可以让上面(1)式的 x1(mod 3)= 2、(mod 5)= 0
再加上(2)式中的 x2(mod 5)= 3,x2(mod 3) = 0
这样是不是 x1 + x2 = 2(mod 3),x1 + x2 = 3(mod 5)
二、然后我们来看,三个数的情况
1.找出三个数:3和5公倍数中找出被7除余1的最小数为15,从3和7的公倍数中找出被5除余1的最小数为21,最后从5和7的公倍数中找出除3余1的最小数70。
2.用15乘2(得到30的是除7余2的最终结果),用21乘3(得到的63是除5余2的最终结果),用70乘2(140是除3余2的结果)。
3.用(30 + 63 + 140)= 233 除以3、5、7的最小公倍数,得到余数为23,就符合题意。
解释一下:
除3余2的数:(3k + 2) = a1
除5余3的数:(5k + 3) = a2
除7余3的数:(7k + 3) = a3
但是,你需要找3个数的同余数,就用到这个公式:(3k + 5i + 7j)%gcd(3,5,7) = 233%105 = 23
然后就可以引入定理,怎么求同余数
【中国剩余定理】
定理简介:
设m1,m1…mk是两辆互质的正整数,则同余方程组:
x = a1(mod m1)
x = a2(mod m2)
………………
x = ak(mod mk)
存在唯一最小整数解使得方程成立。
解决上述问题,首先设N1,N2…Nk满足如下条件:
N1 能被 m2,m3…,mk 整除,而且除以 m1 正好余 1
N2 能被 m1,m3…,mk 整除,而且除以 m2 正好余 1
…
Nk 能被 m1,m2…,mk-1 整除,而且除以 mk 正好余 1
求出这些数字之后,你就会发现,X = N1*a1 + N2*a2 + … + Nk*ak就是我们要求的一个解。
那么就有,(X + m1 m2…mk)mod(m1 m2…mk)
就是 x 的最小整数解(这里加上 m1m2…mk 的原因是当 X 为负数时也成立)
那么,N1,N2,…,Nk怎么求呢?我们建一个等式来求。
我们令 M = m1m2…mk,因为Ni | (m1m2…mk) (不包含mi) ,
所以有 Ni = M/mi * A(设 A 为任意整数) 。(1)
又因为 Ni mod mi = 1,所以 Ni = mi * B + 1(B为任意整数)。(2)
综上就得出:M/mi*A = mi*B + 1 => M/mi*A + (-mi)*B = 1
因为 m1 -> mk 都是互质的,所以 (-m~i!)与 M/mi 也是质数,即 gcd(-mi,M/mi) = 1
也就是有,(M/mi)*A + (-mi)*B = gcd(-mi,M/mi),其中未知数只有 A 和 B ,这就是拓展欧几里的原型,通过 exgcd 来求,A,B的值。
接下来就是 exgcd 的作用,以及怎么求 exgcd:
首先设,a > b,当 b == 0 时,x = 1,y = 0
然后有, gcd(a,b)= d,d = ax1 + by1,所以有 ax1 + by1 = gcd(a,b)。
bx2 + (a % b)y2 = gcd(b,a%b)
根据欧几里得原理有gcd(a,b) = gcd(b,a%b)
则:ax1 + by1 = bx2 +(b,a%b)y2
即:ax1 + by1 = bx2 +(a-(a/b)*b)y2 = ay2 + b(x2 - (a/b)*y2)
根据恒等定理得:x1 = y2 ;y1 = x2 - (a/b)y2
所以,x1 和 y1 的值基于x2 和 y2
int ex_gcd(int a,int b,int &x1,int &y1)
{
if(b == 0){
x1 = 1;
y1 = 0;
return a;
}
int r = ex_gcd(b,a%b,x1,y1);
int x2 = x1;
x1 = y1;
y1 = x2 - a/b*y1;
}
求出 a1 和 a2 的最大公倍数,然后利用他们的系数,求他们的同余数
然后到这一步就知道怎么求出 (M/mi)*A + (-mi)*B = gcd(-mi,M/mi) 的解
然后带回 Ni设的原型中 Ni = M/mi*A
这样 Ni求出来了
于是 for 循环一下,就可以求出 X = N1*a1 + N2*a2 + … + Nk*ak 。最后 X 的值求出来了
代码:
#include<stdio.h>
const int maxn = 100;
int exgcd(int a,int b,int &x,int &y){
if(!b){
x = 1;y = 0;return a;
}
else{
int d = exgcd(b,a%b,y,x);
y -= x*(a/b);
return d;
}
}
int china(int m[],int a[],int n)//w为除数,b为余数,k为有多少式子
{
int ans=0,x,y,M=1;
for(int i = 1;i <= n;i++)
M *= m[i];
for(int i = 1;i <= n;i++)
{
exgcd(M/m[i],m[i],x,y);
ans = (ans + x*M/m[i]*a[i])%M;
}
return (ans%M + M)%M;//寻找最小整数解
}
int main()
{
int a[maxn+10]; //表示所有余数
int m[maxn+10]; //表示所有取模的数
int n; //表示同余的个数
scanf("%d",&n);
for(int i = 1;i <= n;i++){
scanf("%d %d",&m[i],&a[i]);
}
int x = china(m,a,n);
printf("%d\n",x);
return 0;
}