中国剩余定理 & 扩展中国剩余定理

@Pelom  October 11, 2021

中国剩余定理

一、中国剩余定理(CRT)

求解同余方程组

$$ \left\{ \begin{aligned} & x \equiv a_1 \ (\bmod \ m_1) \\ & x \equiv a_2 \ (\bmod \ m_2) \\ & \dots \\ & x \equiv a_n \ (\bmod \ m_n) \end{aligned} \right. $$

其中 $m_1,m_2,\dots,m_n$ 为两两互素的整数。

1.求解

记 $$M=\prod\limits_{i=1}^n m_i$$ 即 $M$ 为所有 $m_i$ 的最小公倍数
记 $$M_i=\frac{M}{m_i}$$ 即 $M_i$ 为除 $m_i$ 外所有 $m$ 的倍数
记 $t_i$ 为 $M_i$ 的逆元,即 $$t_iM_i \equiv 1 \ (\bmod \ m_i)$$
可构造出该方程的特解为 $$x=\sum\limits_{i=1}^n a_it_iM_i$$
则通解为 $$x=kM+\sum\limits_{i=1}^n a_it_iM_i,k \in \mathbb{Z}$$

2.证明

因为$$\forall \ i,j \in [1,n],i \neq j$$ 有 $$(m_i,m_j)=1$$
所以$$(M_i,m_i)=1$$
所以 $\exists \ t_i$ 使得 $$t_iM_i \equiv 1 \ (\bmod \ m_i)$$
所以$$a_it_iM_i \equiv a_i \ (\bmod \ m_i)$$
而 $\forall \ j \in [1,n],i \neq j$ 满足 $$a_it_iM_i \equiv 0 \ (\bmod \ m_j)$$
所以$\forall \ i \in [1,n]$有$$\sum\limits_{i=1}^n a_it_iM_i \equiv a_i \ (\bmod \ m_i)$$
另外,若 $x_1,x_2$ 均为该方程组的解
所以$\forall \ i \in [1,n]$ 有 $$x_1-x_2 \equiv 0 \ (\bmod \ m_i)$$
又 $m_i$ 两两互素
所以$$M \mid x_1-x_2$$
所以方程任意两解间必然相差 $M$ 的整数倍。

3.实现

对于 $t_iM_i \equiv 1 \ (\bmod \ m_i)$ 可用扩展欧几里得算法求解。

void exgcd(int a,int b,int& x,int& y){
    if(b==0){
        x=1;
        y=0;
        return;
    }
    exgcd(b,a%b);
    int t=x;
    x=y;
    y=t-x/y*y;
}
inline int crt(){
    int M=1,res=0,Mi,x,y;
    for(int i=1;i<=n;i++)
        M*=m[i];
    for(int i=1;i<=n;i++){
        Mi=M/m[i];
        exgcd(Mi,m[i],x,y);
        (x%=m[i]+=m[i])%=m[i];
        (res+=a[i]*x*Mi)%=M;
    }
    return res<0?res+M:res;
}

二、扩展中国剩余定理(exCRT)

求解同余方程组

$$ \left\{ \begin{aligned} & x \equiv a_1 \ (\bmod \ m_1) \\ & x \equiv a_2 \ (\bmod \ m_2) \\ & \dots \\ & x \equiv a_n \ (\bmod \ m_n) \end{aligned} \right. $$

其中 $m_1,m_2,\dots,m_n$ 为不一定两两互素的整数,求 $x$ 的最小非负整数解。

1.求解

假设已经求出前 $k-1$ 个方程组成的同余方程组的特解为 $x$ ,且有 $$M=\prod\limits_{i=1}^{k-1} m_i$$
则其通解为 $$x+tM,t \in \mathbb{Z}$$
对于第 $k$ 个方程,需要求满足 $$x+tM \equiv a_k \ (\bmod \ m_k)$$ 的正整数 $t$
此同余方程可用扩展欧几里得算法求解
当任意一个同余方程无解时,该同余方程组无解;否则,前 $k$ 个方程组成的同余方程组的特解为 $$x'=x+tM$$
由数学归纳法,该同余方程组的特解可通过 $n$ 次扩展欧几里得算法求得

int exgcd(int a,int b,int& x,int& y){
    if(b==0){
        x=1;
        y=0;
        return a;
    }
    int g=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return g;
}
inline int excrt(){
    int M=m[1],ans=a[1],g,x,y,c;
    for(int i=2;i<=n;i++){
        c=(a[i]-ans%m[i]+m[i])%m[i];
        g=exgcd(M,m[i],x,y);
        if(c%g!=0)
            return -1;
        x=mul(x,c/g,m[i]/g);
        ans+=x*M;
        M*=m[i]/g;
        ((ans%=M)+=M)%=M;
    }
    return ((ans%=M)+=M)%=M;
}

添加新评论