中国剩余定理

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

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

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

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

找到一个 \(x\) 满足上述条件当然并不容易,但是可以找到一组 \(n_1,n_2,n_3\) 满足
那么直接令 \(x = n_1 + n_2 + n_3\) 可以吗?
显然不行
那么令 \(x=n_{1}+n_{2}+n_{3}\) 可以吗 ? 那恐怕末必。在什么情况下 \(n_{1} \equiv 2 \quad(\bmod 3)\) 可以 推出 \(n_{1}+n_{2} \equiv 2 \quad(\bmod 3)\) 呢? 显然,那只有当 \(n_{2}\)\(3\) 的倍数时成立。同理,要使 \(n_{1}+n_{2}+n_{3}\) 也符合前式,需要 \(n_{2}\)\(n_{3}\) 都是 \(3\) 的倍数。
这样推下去, \(x=n_{1}+n_{2}+n_{3}\) 符合方程组的条件是 \(n_{1}\)\(35\) 的倍数, \(n_{2}\)\(21\) 的倍数, \(n_{3}\)\(15\) 的倍数。也就是说,现在我们只需要解三个同余方程 :

\(\left\{\begin{array}{l} 35 m_{1} \equiv 2(\bmod 3) \\ 21 m_{2} \equiv 3(\bmod 5) \\ 15 m_{3} \equiv 2(\bmod 7) \end{array}\right.\)

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

\(\left\{\begin{array}{ll} 35 w_{1} \equiv 1 & (\bmod 3) \\ 21 w_{2} \equiv 1 & (\bmod 5) \\ 15 w_{3} \equiv 1 & (\bmod 7) \end{array}\right.\)

解得 \(w_{1}=2, w_{2}=1, w_{3}=1\) ,然后可得 \(\left\{\begin{array}{l}m_{1}=2 w_{1}=4 \\ m_{2}=3 w_{2}=3 \\ m_{3}=2 w_{3}=2\end{array}\right.\) ,于是

\(\left\{\begin{array}{l} n_{1}=35 m_{1}=140 \\ n_{2}=21 m_{2}=63 \\ n_{3}=15 m_{3}=30 \end{array}\right.\)

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

我们设 $ M=\prod_{i=1}^{n} a_{i}$,并设 \(r_{i}=\frac{M}{a_{i}}\)。于是 \(w_{i}=\left.\operatorname{inv}\left(r_{i}\right)\right|_{a_{i}}\) , $m_{i}=b_{i} w_{i} $ ,而 $n_{i}=r_{i} m_{i} $,所有 \(n_{i}\) 相加即得 \(x\)
我们把以上这些综合成一个公式即是 :

\(\left.x \equiv \sum_{i=1}^{n} b_{i} r_{i}\left[r_{i}\right]^{-1}\right|_{a_{i}}(\bmod M)\)

代码实现
【模板】中国剩余定理(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 @ 2022-05-26 16:45  0xFF_qwq  阅读(213)  评论(0)    收藏  举报