中国剩余定理
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
即,一个整数除以三余二,除以五余三,除以七余二,求这个整数。《孙子算经》中首次提到了同余方程组问题,以及以上具体问题的解法,因此在中文数学文献中也会将中国剩余定理称为孙子定理。
中国的古人在很早的时候就发现了此题的解法:
三人同行七十稀,五树梅花廿一枝,七子团圆正半月,除百零五便得知。
具体操作流程此处不过多赘述。
这个被叫做“物不知数”的问题本质上是解下面的同余方程组:
这一方程组有解的一个充分条件是 \(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;
}
浙公网安备 33010602011771号