中国剩余定理

有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
即,一个整数除以三余二,除以五余三,除以七余二,求这个整数。《孙子算经》中首次提到了同余方程组问题,以及以上具体问题的解法,因此在中文数学文献中也会将中国剩余定理称为孙子定理。

中国的古人在很早的时候就发现了此题的解法:
三人同行七十稀,五树梅花廿一枝,七子团圆正半月,除百零五便得知。
具体操作流程此处不过多赘述。

这个被叫做“物不知数”的问题本质上是解下面的同余方程组:

这一方程组有解的一个充分条件是 a1,a2an 两两互质,并用构造法给出了这种情况下方程的通解。
回到刚才的原问题:找到 x 使得

找到一个 x 满足上述条件当然并不容易,但是可以找到一组 n1,n2,n3 满足
那么直接令 x=n1+n2+n3 可以吗?
显然不行
那么令 x=n1+n2+n3 可以吗 ? 那恐怕末必。在什么情况下 n12(mod3) 可以 推出 n1+n22(mod3) 呢? 显然,那只有当 n23 的倍数时成立。同理,要使 n1+n2+n3 也符合前式,需要 n2n3 都是 3 的倍数。
这样推下去, x=n1+n2+n3 符合方程组的条件是 n135 的倍数, n221 的倍数, n315 的倍数。也就是说,现在我们只需要解三个同余方程 :

{35m12(mod3)21m23(mod5)15m32(mod7)

注意到模数两两互质,则 gcd(35,3)=gcd(21,5)=gcd(15,7)=1 ,所以我们可以用 拓展欧几里得的方法解

{35w11(mod3)21w21(mod5)15w31(mod7)

解得 w1=2,w2=1,w3=1 ,然后可得 {m1=2w1=4m2=3w2=3m3=2w3=2 ,于是

{n1=35m1=140n2=21m2=63n3=15m3=30

接下来不妨将上述过程公式化

我们设 M=i=1nai,并设 ri=Mai。于是 wi=inv(ri)|aimi=biwi ,而 ni=rimi,所有 ni 相加即得 x
我们把以上这些综合成一个公式即是 :

xi=1nbiri[ri]1|ai(modM)

代码实现
【模板】中国剩余定理(CRT)
一.

int a[N],b[N];
unsigned int M = 1 , t[N] , ans;
signed main(){
	int n = read();
	for(int i=1;i<=n;i++){
		a[i] = read() , b[i] = read();
		M *= a[i];
	}
	for(int i=1;i<=n;i++){
		t[i] = M/a[i];
	}
	for(int i=1;i<=n;i++){
		for(unsigned int j=t[i];j<=0x7f7f7f7f7f7f;j+=t[i]){
			if(j % a[i] == 1){
				ans += j * b[i] % M;
				break;
			}
		}
	}
	printf("%lld\n",ans % M);
	return 0;
}

二.

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define int long long

using namespace std;

const int N = 55;

inline int read(){
	int x=0,f=1;char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
	for(; isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';
	return x*f;
}

int n,a[N],b[N],M=1;
inline void exgcd(int a,int b,int &x,int &y){
	if(b == 0){	
		x=1;y=0;
	}
	else{
		exgcd(b,a%b,x,y);
		int t = x;
		x = y; 
		y = t-(a/b)*y;
	}
}
inline int CRT(){
	int ans = 0,T,x,y;
	for(int i=1;i<=n;++i){
		T = M / a[i];
		exgcd(T,a[i],x,y);
		ans = ((ans + T * x * b[i]) % M + M) % M;
	}
	return (ans + M) % M;
}
signed main(){
	n = read();
	for(int i=1;i<=n;++i){
		a[i] = read() , b[i] = read();
		M *= a[i];
	}
	printf("%lld\n",CRT());
	return 0;
}
posted @   0xFF_qwq  阅读(178)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示