中国剩余定理_解一次同余方程组

问题

孙子定理是中国古代求解一次同余式组(见同余)的方法。是数论中一个重要定理。又称中国余数定理。一元线性同余方程组问题最早可见于中国南北朝时期(公元5世纪)的数学著作《孙子算经》卷下第二十六题,叫做“物不知数”问题,原文如下:
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?即,一个整数除以三余二,除以五余三,除以七余二,求这个整数。《孙子算经》中首次提到了同余方程组问题,以及以上具体问题的解法,因此在中文数学文献中也会将中国剩余定理称为孙子定理。
抽象来说,就是已知m和a求解一次同余方程
{ x 三 m 1 ( m o d   a 1 ) x 三 m 2 ( m o d   a 2 ) . . . x 三 m n ( m o d   a n ) \begin{cases} x三m_1(mod \ a_1) \\ x三m_2(mod \ a_2) \\ ...\\ x三m_n(mod \ a_n) \end{cases} xm1(mod a1)xm2(mod a2)...xmn(mod an)

思路

由于由n个同余方程,要求直接解上式较为复杂,所以先考虑只有两个同余方程的情况。
{ x 三 m 1 ( m o d   a 1 ) x 三 m 2 ( m o d   a 2 ) \begin{cases} x三m_1(mod \ a_1) \\ x三m_2(mod \ a_2) \\ \end{cases} { xm1(mod a1)xm2(mod a2)
一般求解同余方程都要先将同余方程转换为一般方程的格式。上式可以转换为。
{ x = k 1 ∗ a 1 + m 1 x = k 2 ∗ a 2 + m 2 \begin{cases} x=k_1*a_1+m_1 \\ x=k_2 *a_2+m_2 \\ \end{cases} { x=k1a1+m1x=k2a2+m2
可得
k 1 ∗ a 1 + m 1 = k 2 ∗ a 2 + m 2 k_1*a_1+m_1=k_2 *a_2+m_2 k1a1+m1=k2a2+m2
对方程进行变换后得
k 1 ∗ a 1 − k 2 ∗ a 2 = m 2 − m 1 k_1*a_1-k_2*a_2=m_2-m_1 k1a1k2a2=m2m1
其中, m 2 , m 1 , a 1 , a 2 m_2,m_1,a_1,a_2 m2,m1,a1,a2都是已知量,上式就符合 a x + b y = c ax+by=c ax+by=c的不定方程形式,可以通过扩展欧几里得算法求解。
扩展欧几里得算法解不定方程
对不定方程求解得
{ k 1 = k 01 + k ∗ a 2 d k 2 = k 02 + k ∗ a 1 d \begin{cases} k_1=k_{01}+k* \frac {a_2} d \\ k_2=k_{02}+ k*\frac {a_1} d \\ \end{cases} { k1=k01+kda2k2=k02+kda1
其中 k 1 , k 2 k_1,k_2 k1,k2表示满足上述方程的所有解, k 0 , 1 , k 02 k_{0,1},k_{02} k0,1,k02表示满足方程的一组特解,k是任意非零整数,d是 a 1 , a 2 a_1,a_2 a1,a2的最大公约数
k 1 k_1 k1回代到 x = k 1 ∗ a 1 + m 1 x=k_1*a_1+m_1 x=k1a1+m1,得 x = a 1 ∗ k 01 + k ∗ a 2 a 1 d + m 1 x=a_1*k_{01}+k* \frac {a_2a_1} d+m_1 x=a1k01+kda2a1+m1,其中 a 2 a 1 d \frac {a_2a_1} d da2a1 a 2 , a 1 a_2,a_1 a2,a1的最小公倍数。
于是,上述两个同余方程就被转化为了一个同余方程。
x = k ∗ a 2 a 1 d + x 0 x=k*\frac {a_2a_1} d+x_0 x=kda2a1+x0

x 三 x 0 ( m o d   a 2 a 1 d ) x三x_0(mod \ \frac {a_2a_1} d) xx0(mod da2a1)
其中, x 0 x_0 x0是满足上述同余方程的任意一组解,x是满足同余方程组的所有解。

推广

思路中,证明了两个一阶同余方程组可以被转换为一个一阶同余方程。所以,要求解n个一阶同余方程,可以依次转换,化繁为简。

{ x 三 m 1 ( m o d   a 1 ) x 三 m 2 ( m o d   a 2 ) . . . x 三 m n ( m o d   a n ) = { x 三 x 0 ( m o d   a 2 a 1 d ) x 三 m 3 ( m o d   a 3 ) . . . x 三 m n ( m o d   a n ) = . . . \begin{cases} x三m_1(mod \ a_1) \\ x三m_2(mod \ a_2) \\ ...\\ x三m_n(mod \ a_n) \end{cases}= \begin{cases} x三x_0(mod \ \frac {a_2a_1} d)\\ x三m_3(mod \ a_3) \\ ...\\ x三m_n(mod \ a_n) \end{cases}=... xm1(mod a1)xm2(mod a2)...xmn(mod an)=xx0(mod da2a1)xm3(mod a3)...xmn(mod an)=...
最终可以将上式转换为两个一阶同余方程组,利用扩展欧几里得算法就可以求得正确的解。

例题

在这里插入图片描述

代码模板

#include<iostream>
using namespace std;

typedef long long LL;

LL exgcd(LL a,LL b,LL &x,LL &y){
    
    
	#扩展欧几里得算法
    if(b==0){
    
    
        x=1;
        y=0;
        return a;
    }
    int d = exgcd(b,a%b,y,x);
    y-=(a/b)*x;
    return d;
}

int main(){
    
    
    int n;
    LL a1,m1;
    LL a2,m2,k1,k2;
    cin>>n;
    cin>>a1>>m1;
    for(int i=0;i<n-1;i++){
    
    
        scanf("%d%d",&a2,&m2);
        //求解不定方程,默认方程右边是最大公约数
        int d = exgcd(a1,a2,k1,k2);
        if((m2-m1)%d!=0){
    
    
        	//方程无解
            printf("-1");
            return 0;
        }
        k1 *= (m2-m1)/d;//得到满足条件的一个k0
        LL t = a2/d;
        k1 = (k1%t+t)%t;//寻找满足条件的最小正整数k0
        
        m1 = k1*a1+m1; //更新m1
        a1 = a1/d*a2;//更新a1,先除法是为了防止越界
    }
    printf("%lld",m1);//输入m1
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_45931661/article/details/119949377
今日推荐