中国剩余定理(原理分析,互质与不互质情况的思考)

中国剩余定理模板

中国剩余定理模板的具体描述是这样的:

模型:

  • 给定 n 个 k i k_i ki p i p_i pi,即 n 个条件 。
  • 求解一个最小的 S ,使得满足 S % k i = p i S\%k_i=p_i S%ki=pi
  • 中国剩余定理说明:假设整数 ( k 1 , k 2 , . . . , k n ) (k_1, k_2, ... , k_n) (k1,k2,...,kn) 两两互质,则对任意的整数: ( p 1 , p 2 , . . . , p n ) (p_1, p_2, ... ,p_n) (p1,p2,...,pn),方程组均有解,并且通解可以用如下方式得到:

方法:

  • T 为 k i k_i ki 之积(猪圈周期)
  • m i = T / k i m_i=T/k_i mi=T/ki(除 k i k_i ki 以外之积)
  • t i t_i ti m i m_i mi k i k_i ki 下的逆元,即 t i × m i = 1 ( m o d k i ) t_i\times m_i=1\pmod {k_i} ti×mi=1(modki)
  • < = > t i × m i + k i × y = 1 <=>t_i\times m_i+k_i\times y=1 <=>ti×mi+ki×y=1 求解 t i t_i ti(拓欧,不可用费马)
  • S = ∑ i = 1 n t i × p i × m i S=\sum_{i=1}^n t_i\times p_i\times m_i S=i=1nti×pi×mi
  • 最小解为 S % T S\%T S%T

代码

#include<bits/stdc++.h>
using namespace std;
const int N=1000;
long long x,y,k[N],p[N],m[N],t[N];
void Ex_gcd(long long a,long long b){
	if(b==0){
		x=1;
		y=0;
		return;
	}
	Ex_gcd(b,a%b);
	long long t=x;
	x=y;
	y=t-a/b*y;
}
int main(){
	long long n,T=1,s=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>k[i]>>p[i];
		T*=k[i];
		//S%k[i]=p[i]
	}
	for(int i=1;i<=n;i++)m[i]=T/k[i];
	for(int i=1;i<=n;i++){
		Ex_gcd(m[i],k[i]);
		//求解m[i]模k[i]的乘法逆元m[i]*t[i]=1(mod k[i])
		//即m[i]*t[i]+k[i]*y=1 
		t[i]=(x%k[i]+k[i])%k[i];//求解最小t[i]
		s+=t[i]*p[i]*m[i];
	}
	cout<<s%T;
}

模板题1
模板题2

深入研究中国剩余定理的实现过程

  • 中国剩余定理成立的条件是:k数组中任意两数互质
    ,那不互质该怎么求?
  • 什么时候无解?
  • 什么原理???

原理分析

  • 假设只有3个式子,并设n1,n2,n3为各式满足条件的一个解
    x%k1=p1,n1%k1=p1
    x%k2=p2,n2%k2=p2
    x%k3=p3,n3%k3=p3

  • 根据同余定理:(x+bk)%k=p
    我们一个条件一个条件变多的来思考这个问题?

  • 对于式子x%k1=p1,我们如何求得满足条件一个n1?
    求一个x%k1=1即:x ≡ \equiv 1(mod k1),(拓展欧几里得求逆元即可),再乘上p1即可求得一个n1

  • 已知n1%k1=p1,要想让(n1+n2)%k2=p2,则根据同余定理,n1是k2的倍数。已知n1%k1=p1,(n1+n2)%k2=p2,要想让(n1+n2+n3)%k3=p3,则n1+n2要是k3的倍数,我们考虑让n1和n2都是k3的倍数。

  • 以此类推,设T为k1的积。
    n1需要是T/k1的倍数,n2需要是T/k2的倍数………
    最后我们将所有的n加起来就好了,就得到一个满足的s

  • 重新整理一下过程,先用拓展欧几里得,并乘上pi得到一个满足第 i 式的ni,再乘上除 ki 以外的所有数的最小公倍数,则此时的 ni 是一个其他任何式子加上它,都不影响其他式子符合题意的一个值,且 ni 满足 i 式。将所有的 ni 相加,则此时的sum(n)符合所有式子。

  • 要注意的是:sum(n)并不是唯一解,sum(n)+nlcm都是解,则最小解即为:sum(n)%lcm

  • 当所有 k 互质的时候,最小公倍数就是除 ki 以外所有数的积。

k数组不互质

  • 很显然,只要存在任意两个数不互质,以上的中国剩余定理就崩了。不行,我们需要去学习新方法。
  • 我学到的一个方法是:将 n 个式子不停的两两合并,得到符合全部式子的 X 。

怎么个合并法?

  • x ≡ \equiv 2 (mod 4)
    x ≡ \equiv 4 (mod 6)
    => x ≡ \equiv 10 (mod 12)
    牛!!!

  • 我们不断的将前n-1个式子合并成的式子和第n个式子合并,合并完所有之后,x ≡ \equiv p (mod k),此时 p 就是解

如何合并两个同余式子???

  • x ≡ \equiv p1(mod k1)
    x ≡ \equiv p2(mod k2)
  • 则 x=k1x1+p1;x=k2x2+p2
  • => k1x1+p1 = k2x2+p2
  • => k1x1+k2x2 = p2 - p1
  • 根据裴蜀定理,当gcd(k1,k2)|(p2-p1)时,方程才有解
    我们可以根据合并的过程中是否出现了不整除的情况才判断该方程是否有解。
  • 继续分析,设 gcd(k1,k2) = d,p2-p1 = c
  • => k 1 d \frac{k1}{d} dk1x1+ k 2 d \frac{k2}{d} dk2x2 = c d \frac{c}{d} dc
  • 此时 k 1 d \frac{k1}{d} dk1 k 2 d \frac{k2}{d} dk2互质,可用拓展欧几里得求得 k 1 d \frac{k1}{d} dk1x1+ k 2 d \frac{k2}{d} dk2x2 = 1的最小整数解 x1。进而求 k 1 d \frac{k1}{d} dk1x1+ k 2 d \frac{k2}{d} dk2x2 = c d \frac{c}{d} dc的最小整数解x1
  • 再带回去求得 X = k1x1+p1
  • 而新的同余式就是:x ≡ \equiv k1x1+p1(mod k 1 k 2 d \frac{k1k2}{d} dk1k2
    => 即同余式仍保持着 x ≡ \equiv p(mod k)
  • 可以看出:最后的解为 p,k始终为前 n 个 ki 的lcm。
  • 为方便书写:我们设前 i-1 个式子合并后成的同余式子为:
    x ≡ \equiv X(mod lcm)
    在与第 i 式合并的时候即与 x ≡ \equiv pi(mod ki)合并
  • 则合并后的同余式子为:x ≡ \equiv X+x1lcm (mod l c m k i d \frac{lcmki}{d} dlcmki)
  • 证明如下:不会证明
    k 1 d \frac{k1}{d} dk1x1+ k 2 d \frac{k2}{d} dk2x2 = c d \frac{c}{d} dc
    => k 1 d \frac{k1}{d} dk1x1 ≡ \equiv c d \frac{c}{d} dc(mod k 2 d \frac{k2}{d} dk2)

代码如下:

#include<bits/stdc++.h>
using namespace std;

long long p[200010],k[200010],n;

long long x,y;
void ex_gcd(long long a,long long b){
	if(b==0){
	    x=1;
	    y=0;
	    return;
	}
	ex_gcd(b,a%b);
	long long t=x;
	x=y;
	y=t-a/b*y;
}
long long gcd(long long a,long long b){
	if(b==0)return a;
	else return gcd(b,a%b);
}
long long China2() {
	long long X=p[1];//x%lcm=X
	long long lcm=k[1];
	
	for(int i=2;i<=n;i++){
		long long c=p[i]-X;
		long long d=gcd(lcm,k[i]);
		if(c%d!=0)return -1;//裴蜀定理判断 
		ex_gcd(lcm/d,k[i]/d);//求lcm/d*x1+k[i]/d*x2=1的一组解x1,x2,并存x1在x 
		
		x=(x*c/d%(k[i]/d)+k[i]/d)%(k[i]/d);//求出lcm/d*x1+k[i]/d*x2=c/d的最小解x 
		
		X+=x*lcm;//更新新合并式子的X 
		lcm=lcm/d*k[i];//更新新合并式子的lcm 
	}
	return X;
}

int main(){
	cin>>n;
	for(int i=1;i<=n;i++)scanf("%lld%lld",&k[i],&p[i]);
	
	cout<<(long long)China2();
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值