中国剩余定理

中国剩余定理

  • 作用及内容
    可以用来求解n组线性同余方程的通解,例如有n个数\(a_1,a_2,a_3\dots a_n\)和n个两两互质的数\(m_1,m_2,m_3\dots m_n\)组成的线性同余方程组的通解\(x=\sum\limits_{i=1}^{n}a_iM_iM^{-1}_i\)\(M_i^{-1}是M_i\)的逆元)

\[\begin {cases} x\equiv a_1(mod\enspace m_1) \\ x\equiv a_2(mod\enspace m_2) \\ x\equiv a_3(mod\enspace m_3) \\ \dots \\ x\equiv a_n(mod\enspace m_3)\\ \end {cases} \]

-数学证明
\(M_i=\frac{m_1m_2m_3\dotsm_n}{m_i}\),因为\(m_i\)之间两两互质所以\(M_i\)\(m_i\)互质则\(M_i\)有逆元\(M_i^{-1}(mod\enspace m_i)\),则有\(M_iM_i^{-1}\equiv1(mod\enspace m_i)\)
通解为:\(x=\sum\limits_{i=1}^{n}a_iM_iM^{-1}_i\),任取i=k,由于除了\(M_K,M_i\)都是\(m_i\)的倍数所以\(M_i\%m_i=0\)则此时有
\(x=\sum\limits_{i=1}^{n}a_iM_iM^{-1}_i=a_kM_kM_k^{-1}\)符合方程,证毕。

扩展中国剩余定理

  • 作用
    与中国剩余定理类似,只不过可以处理\(m_i\)之间不互质的情况。

\[\begin {cases} {} x\equiv a_1(mod\enspace m_1) &(1) \\ x\equiv a_2(mod\enspace m_2) &(2)\\ x\equiv a_3(mod\enspace m_3)&(3) \\ \dots \\ x\equiv a_n(mod\enspace m_3) &(n)\\ \end {cases} \]

  • 内容及数学推导
    满足所有方程的x则必定得满足前两个方程,我们不妨先取前两个方程来看。\((1)(2)连立\Longrightarrow k_1m_1+a_1=k_2m_2+a_2\Longleftrightarrow k_1m_1-k_2m_2=a_2-a_1\),由欧几里得算法,当\(a_2-a_1\)是gcd(m1,-m2)的倍数时\(k_1,k_2\)有解否则无解,如果此时无解则整个方程组必定无解。当有解时,由欧几里得算法,\(k_1的一个特解为k_1=\frac{(a_2-a_1)}{gcd(m1,-m2)}\)通解为\(k_1=k_1+k\frac{m_2}{gdc(m_1,m_2)}\)将通解代入方程(1)的等价式子中可得(\(x=k_1m_1+a_1+k\frac{m_1m_2}{gcd(m1,m2)})\)\(k_1m_1+a_1=m,k[m1,m2]=m\)[1],这样我们通过合并(1)(2)得到了一个类似与方程(1)(2)的新方程,我们可以重复这个过程n-1此就可以将n个方程合并为一个方程\(x\equiv a(mod\enspace m)\),求解最小解x只需要求\(a\%m\),因为a和x在模m的情况下是相等的。
    -代码实现
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
LL exgcd(LL a,LL b,LL &x,LL &y)
{
    if(!b)
    {
        x=1,y=0;
        return a;
    }
    LL d=exgcd(b,a%b,y,x);
    y=y-a/b*x;
    return d;
}
int main()
{
    int n;
    cin>>n;
    LL a1,m1;
    cin>>a1>>m1;
    for(int i=0;i<n-1;i++)
    {
        LL a2,m2;
        scanf("%lld %lld",&a2,&m2);
        LL k1,k2;
        LL d=exgcd(a1,a2,k1,k2);//应该传-a2的,但是为了保证d不为负数从而使得t不为负数,就传a2,而且后面更新也没用用到k2,所以对结果没有影响
        if((m2-m1)%d)
        {
            puts("-1");
            return 0;
        }
        k1=k1*(m2-m1)/d;
        LL t=a2/d;
        k1=(k1%t+t)%t;
        m1=k1*a1+m1;//注意先更新m1再更新a1
        a1=a1*a2/d;
        
    }
    printf("%lld",m1%a1);
    
    
    return 0;
}

  1. 补充知识:对于两个数a,b有:\(\frac{a*b}{gcd(a,b)}=[a,b],[a,b]\)是a,b的最小公倍数,原理可联想小学求两个数的最小公倍数的方法,其实就是把两个数所有的因子相乘其中如果对于公因子就只乘一遍。 ↩︎

posted @ 2023-07-01 11:48  Taco_gu  阅读(66)  评论(0)    收藏  举报