您的位置:首页 > 其它

【学习笔记】中国剩余定理

2017-10-17 14:03 411 查看

感觉之前写的太垃圾了,重新写一下。

没有那么多奇怪的介绍,直接开始吧。

设m1,m2,m3,m4....mk两两互素,则同余方程组

x≡a1(mod m1)

x≡a2(mod m2)

x≡a3(mod m3)

x≡a4(mod m4)

x≡ak(mod mk)

一定有解,x≡(a1*M1*M1^(-1)+a2*M2*M2^(-1)+....)(mod M)

其中M=m1*m2*m3*....mk,Mi=M/mi,Mi^(-1)是Mi在模mi意义下的逆元。

互素情况下的模板,是POJ1006代码

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;

int a[4],m[4];
int p,e,i,d,t=1;

void exgcd(int a,int b,int &x,int &y){
if(b==0){
x=1;y=0;
return;
}
exgcd(b,a%b,x,y);
int t;
t=x;x=y;y=t-a/b*y;
}

int CRT(int a[],int m[],int n){
int M=1,ans=0;
for(int i=1;i<=n;i++)M*=m[i];//求出M
for(int i=1;i<=n;i++){
int x,y;
int Mi=M/m[i];
exgcd(Mi,m[i],x,y);//扩展欧几里得求逆元
ans=(ans+Mi*x*a[i])%M;//统计答案
}
if(ans<0)ans+=M;
return ans;
}

int main(){
while(cin>>p>>e>>i>>d){
if(p==-1&&e==-1&&i==-1&&d==-1)break;
a[1]=p;a[2]=e;a[3]=i;
m[1]=23;m[2]=28;m[3]=33;
int ans=CRT(a,m,3);
if(ans<=d)ans+=21253;
}
return 0;
}

接下来是m1,m2,m3,m4...mk不互素的情况。

主要的做法是将式子合并。

对于

x mod m1=a1

x mod m2=a2

x mod m3=a3

可以将前两个式子写成

x=k1*m1+a1--------- (1)

x=k2*m2+a2----------(2)

那么k1*m1+a1=k2*m2+a2

整理得:

k1*m1-k2*m2=a2-a1,

对于上面这个式子,其实就是ax+by=k。

用扩展欧几里得求出k1之后,代入(1),就可以求出x。

这里求的x是(1)、(2)两式的解,通解是x'=x+k*lcm(m1,m2)。

将通解的式子变形就是x' mod lcm(m1,m2)=x。这个式子就是我们将前两个方程

合并后的式子。

令M=lcm(m1,m2),A=x*m1+a1.

那么新的方程就是 x' mod M =A

n个方程一直合并就可以了,最后剩下一个方程

x mod M=A,其中M=lcm(m1,m2,m3....mk)。

所求x是方程组的一个特解,通解为x'=x+k*M。

注意:没有解的条件是gcd(m1,m2)|a2-a1是不成立的。

如果要求最小正整数解的话:

x%=M;if(x<0)x+=M;

代码:

POJ2891

#include<iostream>
#include<cstdio>
#include<cstring>
#define LL long long
#define maxn 100009
using namespace std;

int n;
LL m[maxn],r[maxn];

LL exgcd(LL a,LL b,LL &x,LL &y){
if(b==0){
x=1;y=0;
return a;
}
LL t,r=exgcd(b,a%b,x,y);
t=x;x=y;y=t-a/b*y;
return r;
}

LL CRT(LL m[],LL r[],LL n){
LL x,y,gcd,M=m[1],R=r[1];
for(int i=2;i<=n;i++){
LL gcd=exgcd(M,m[i],x,y);
if((r[i]-R)%gcd)return -1;
x=(r[i]-R)/gcd*x%(m[i]/gcd);
R+=x*M;//新的余数R=r1+x*m[1]
M=M/gcd*m[i];//新的M,lcm(m1,m2)
R%=M;
}
return R>0?R:R+M;
}

int main(){
while(~scanf("%d",&n)){
for(int i=1;i<=n;i++)
scanf("%lld%lld",&m[i],&r[i]);
printf("%lld\n",CRT(m,r,n));
}
return 0;
}

 

 


 

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: