中国剩余定理CRT(孙子定理)
Posted bennettz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了中国剩余定理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.$
证明:
因为$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; }
以上是关于中国剩余定理CRT(孙子定理)的主要内容,如果未能解决你的问题,请参考以下文章