我们上一章节中,详细地讲解了如何利用扩展欧几里德算法解一个线性同余方程,但是如果我们遇到了线性同余方程组的话,我们就需要用到今天所讲解的中国剩余定理。
但是中国剩余定理的成立前提是,方程组中的模数两两互质。
对于方程组:
{ x ≡ a 1 ( m o d m 1 ) x ≡ a 2 ( m o d m 2 ) x ≡ a 3 ( m o d m 3 ) . . . x ≡ a n ( m o d m n ) \begin{cases} x\equiv a_1(mod\ m_1)\\ x \equiv a_2(mod \ m_2)\\ x \equiv a_3(mod\ m_3)\\ ...\\ x \equiv a_n(mod\ m_n) \end{cases} ⎩ ⎨ ⎧x≡a1(mod m1)x≡a2(mod m2)x≡a3(mod m3)...x≡an(mod mn)
最终的结果
x = ∑ k = 1 n a k c k c k − 1 x=\sum_{k=1}^{n}a_kc_kc_k^{-1} x=k=1∑nakckck−1
其中:
M = ∏ k = 1 n m k M=\prod_{k=1}^{n}m_k M=k=1∏nmk
c k = M m k c_k=\frac{M}{m_k} ck=mkM
c k − 1 c_k^{-1} ck−1是模数为 m k m_k mk的时候, c k c_k ck的逆元。
我们先证明一下,逆元 c k − 1 c_k^{-1} ck−1是存在的。
因为 m m m之间是互质的,而 c k c_k ck中不含有 m k m_k mk。因此, g c d ( c k , m k ) = 1 gcd(c_k,m_k)=1 gcd(ck,mk)=1
由裴蜀定理可知:
存在 x c k + y m k = 1 xc_k+ym_k=1 xck+ymk=1
即: x c k = − y m k + 1 xc_k=-ym_k+1 xck=−ymk+1
即: x c k ≡ 1 ( m o d m k ) xc_k\equiv1(mod\ m_k) xck≡1(mod mk)
此时的 x x x就是逆元 c k − 1 c_k^{-1} ck−1,所以逆元必定是存在的。
假设一个 c i c_i ci
当 i = = k i \ ==k i ==k的时候,
c i ∗ c i − 1 ≡ 1 m o d ( m k ) c_i*c_i^{-1}\equiv 1 mod(m_k) ci∗ci−1≡1mod(mk)
两侧同时乘 a i a_i ai
a i c i ∗ c i − 1 ≡ a i m o d ( m k ) a_ic_i*c_i^{-1}\equiv a_i\ mod(m_k) aici∗ci−1≡ai mod(mk)
当 i ! = k i \ !=k i !=k的时候,
此时的 c i c_i ci当中是存在 m k m_k mk的,不存在的是 m i m_i mi
由于 c i c_i ci包含了 m k m_k mk,所以此时 c i ∣ m k c_i|m_k ci∣mk
即 c i % m k = 0 c_i\%m_k=0 ci%mk=0
两边同时乘以 a i c i − 1 a_ic_i^{-1} aici−1还是 0 0 0
因此,综合上述两种情况:
我们的 x % m k = 0 + 0 + . . . + a k c k c k − 1 + 0 x\%m_k=0+0+...+a_kc_kc_k^{-1}+0 x%mk=0+0+...+akckck−1+0
即:
x ≡ a k c k c k − 1 m o d ( m k ) x\equiv a_kc_kc_k^{-1}mod(m_k) x≡akckck−1mod(mk)
因为在第一种情况下,我们得到了:
a k c k ∗ c k − 1 ≡ a k m o d ( m k ) a_kc_k*c_k^{-1}\equiv a_k\ mod(m_k) akck∗ck−1≡ak mod(mk)
所以:
x ≡ a k m o d ( m k ) x\equiv a_kmod(m_k) x≡akmod(mk)
符合我们的方程组。因此这个结论是正确的。
第一步:计算 M M M
第二步:计算 c i c_i ci
第三步:计算 c i − 1 c_i^{-1} ci−1(利用扩展欧几里德算法)
这里讲解一下如何计算逆元 c i − 1 c_i^{-1} ci−1
c i ∗ c i − 1 ≡ 1 ( m o d m i ) c_i*c_i^{-1}\equiv 1(mod\ m_i) ci∗ci−1≡1(mod mi)
即:
c i c i − 1 + y m i = 1 c_ic_i^{-1}+ym_i=1 cici−1+ymi=1
这个式子中,我们不知道的未知数有两个,一个是 c i − 1 c_i^{-1} ci−1,另一个是 y y y。
但我们知道的是: c i , m i c_i,m_i ci,mi
所以我们可以用扩展欧几里德算法+裴蜀定理
计算出 x ∗ c i + y ∗ m i = g c d ( c i , m i ) x*c_i+y*m_i=gcd(c_i,m_i) x∗ci+y∗mi=gcd(ci,mi)
而 g c d ( c i , m i ) = 1 gcd(c_i,m_i)=1 gcd(ci,mi)=1
所以,我们用这两个式子求的就是,我们的目标变量
c i − 1 c_i^{-1} ci−1和 y y y
也就是说,我们的 x = c i − 1 x=c_i^{-1} x=ci−1
第四步:套用中国剩余定理
#include
using namespace std;
typedef long long LL;
const int N=1e5+10;
int n;
LL m[N],a[N];
//扩展欧几里德算法
LL exgcd(LL a,LL b,LL&x,LL&y)
{
if(b==0)
{
x=1,y=0;
return a;
}
else
{
LL res=exgcd(b,a%b,x,y);
LL tmp=y;
y=x-a/b*y;
x=tmp;
return res;
}
}
//中国剩余定理
LL CRT(LL m[],LL a[])
{
LL M=1,ans=0;
//计算M
for(int i=1;i<=n;i++)M*=m[i];
for(int i=1;i<=n;i++)
{
//得到ci
LL c=M/m[i],x,y;
//计算逆元
exgcd(c,m[i],x,y);
//利用中国剩余定理公式
ans=(ans%M+a[i]*c*x%M)%M;
}
//返回答案
return (ans%M+M)%M;
}
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&m[i],&a[i]);
}
cout<<CRT(m,a)<<endl;
return 0;
}