中国剩余定理CRT(孙子定理)

中国剩余定理

给出以下的一元线性同余方程组:

$\Large(s):\left\{
\begin{aligned}
x\equiv a_1\ (mod\ m_1)\\
x\equiv a_2\ (mod\ m_2)\\
\vdots\ \ \ \ \ \ \ \ \ \ \ \ \\
x\equiv a_n\ (mod\ m_n)
\end{aligned}
\right.$

假设整数$m_1,m_2……,m_n$两两互质,则对任意的整数:$a_1,a_2……,a_n$,方程组有解
设$M=m_1m_2……m_n$并且$M_i=M/m_i$
则解为$x\equiv(a_1M_1M_1^{-1}+a_2M_2M_2^{-1}+……+a_nM_nM_n^{-1})mod\ M$

证明:

因为$x\equiv(a_iM_iM_i^{-1})mod\ m_i\ \ i\in\{1,2,……,n\}$  $(MiMi^-1)\%mi=1$

又因为$a_jM_jM_j^{-1}\equiv 0\ mod\ m_i\ \ \forall i\neq j$(如果i!=j,则Mj是mi的倍数)

所以 $x\equiv(a_1M_1M_1^{-1}+a_2M_2M_2^{-1}+……+a_nM_nM_n^{-1})mod\ m_i\ \ i\in\{1,2,……,n\}$ 

因为mi互素

所以$x\equiv(a_1M_1M_1^{-1}+a_2M_2M_2^{-1}+……+a_nM_nM_n^{-1})mod\ M$

 

#include<cstdio>
#define ll long long
inline void exgcd(ll a,ll b,ll &x,ll &y){//a,b,x,y同ax+by=gcd(a,b)中的a,b,x,y
    if(!b){
        x=1,y=0;return;
    }
    ll t;
    exgcd(b,a%b,x,y);
    t=x,x=y,y=t-(a/b)*y;
}
inline ll Inverse(ll a,ll p){//求a模p的乘法逆元
    ll x,y;
    exgcd(a,p,x,y);
    return x;
}
inline ll CRT(ll a[],ll m[],ll n){//求解同余方程组 
    ll M=1,_Mi,Mi,ans=0;//_Mi为Mi的乘法逆元(%m[i]意义下的) 
    for(int i=0;i<n;i++)M*=m[i];
    for(int i=0;i<n;i++){
        Mi=M/m[i];
        _Mi=Inverse(Mi,m[i]);
        ans=(ans+Mi*_Mi*a[i])%M;
    }
    if(ans<0)ans+=M;
    return ans;//返回最小正整数解 
}
int main(){
    return 0;
} 

例题

韩信点兵

/******************************************************************
                        中国剩余定理 
******************************************************************/
#include<cstdio>
#define ll long long
#define maxn 15
inline void exgcd(ll a,ll b,ll &x,ll &y){//a,b,x,y同ax+by=gcd(a,b)中的a,b,x,y
    if(!b){
        x=1,y=0;return;
    }
    ll t;
    exgcd(b,a%b,x,y);
    t=x,x=y,y=t-(a/b)*y;
}
inline ll Inverse(ll a,ll p){//求a模p的乘法逆元
    ll x,y;
    exgcd(a,p,x,y);
    return x;
}
inline ll CRT(ll a[],ll m[],ll n,ll &M){//求解同余方程组 
    ll _Mi,Mi,ans=0;//_Mi为Mi的乘法逆元(%m[i]意义下的) 
    for(int i=0;i<n;i++)M*=m[i];
    for(int i=0;i<n;i++){
        Mi=M/m[i];
        _Mi=Inverse(Mi,m[i]);
        ans=(ans+Mi*_Mi*a[i])%M;
    }
    if(ans<0)ans+=M;
    return ans;//返回最小正整数解 
}
ll p[maxn],a[maxn];
ll M=1,ans,n,m;
int main(){
//    freopen("HanXin.in","r",stdin);
//    freopen("HanXin.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for(int i=0;i<m;i++){
        scanf("%lld%lld",p+i,a+i);
    }
    ans=CRT(a,p,m,M);
    if(ans>n)printf("-1");
    else printf("%lld",(n-ans)%M);
    return 0;
}

扩展——求解模数不互质的线性方程组

$\Large (s):\left\{
\begin{aligned}
x\equiv a_1\ (mod\ m_1)\\
x\equiv a_2\ (mod\ m_2)\\
\vdots\ \ \ \ \ \ \ \ \ \ \ \ \\
x\equiv a_n\ (mod\ m_n)
\end{aligned}
\right.$

如果m1,m2……mn不互质,则中国剩余定理无法运用
这时我们可以对方程组进行转换,比如前两个方程可以变为:

$x=a_1+k_1m_1$和$x=a_2+k_2m_2$

则$a_2-a_1=k_1m_1-k_2m_2$

只有k1,k2是未知,我们用扩展欧几里得求出最小的k1(如果a2-a1%gcd(m1,m2)!=0,则无解)

求解最小k1:

设$q*m_1+y*m_2=gcd(m1,m2)$

用exgcd(m1/gcd(m1,m2),m2/gcd(m1,m2),q,y)解出最小q,

最小的$k_1=q*(a_2-a_1)/gcd(m_1,m_2)$;

带入$x=a_1+k_1m_1$可以求出满足前两个方程的解

这时,记当前x为x',

整个方程组的解x一定就满足$x\equiv x'(mod\ lcm(m_1,m_2))$

将这个方程加入方程组,重复上面步骤,每次消去一个方程,最后一个x'就是答案

 注意:m1,m2,……,mn的最小公倍数不要爆容器

#include<cstdio>
#include<cstdlib>
#define ll long long
inline ll gcd(ll a,ll b){
    return b?gcd(b,a%b):a;
}
inline ll lcm(ll a,ll b){
    return a*b/gcd(a,b);
}
inline void exgcd(ll a,ll b,ll &x,ll &y){//a,b,x,y同ax+by=gcd(a,b)中的a,b,x,y
    if(!b){
        x=1,y=0;return;
    }
    ll t;
    exgcd(b,a%b,x,y);
    t=x,x=y,y=t-(a/b)*y;
}
inline void merge(ll a1,ll m1,ll a2,ll m2,ll &a3,ll &m3){//将方程x=a1+k1m1和x=a2+k2m2合并为x=a3+k3m3; 
    ll d=gcd(m2,m1),a=a2-a1,q,y;
    if(a%d)exit(1);//无解
    m3=lcm(m1,m2);
    exgcd(m1/d,m2/d,q,y);
    a3=a/d*q*m1+a1;
    ((a3%=m3)+=m3)%=m3;
} 
inline ll solve(ll a[],ll m[],ll n){//求解同余方程组 
    ll a1=a[0],m1=m[0];
    for(int i=1;i<n;i++){
        merge(a1,m1,a[i],m[i],a1,m1);
    }
    return (a1%m1+m1)%m1;//返回最小正整数解 
}
int main(){
    return 0;
} 

 

posted @ 2017-10-16 08:54  Bennettz  阅读(562)  评论(0编辑  收藏  举报