线性同余方程组

线性同余方程组问题

线性同余方程的形式:ax ≡ b (mod m),意思就是求解x的值使得 (a*x)%m=b%m

一、首先复习一下模运算,对于模运算,有以下等式成立:

假设a ≡ c (mod m)且b ≡ d(mod m),则:

  • a + b ≡ c + d (mod m)
  • a - b ≡ c - d (mod m)
  • a * b ≡ c * d (mod m)

对于除法来说相对复杂一点
虽然2 ≡ 8 (mod 6),但是2/2 = 1 !≡ 4 = 8/2(mod 6)

当a * c ≡ b * c (mod m)时 (a-b) * c 能被m整除
我们假设d = gcd(c,m)
(a-b) * (c/d)就能被m/d整除
又因为c/d和m/d互为素数
所以a-b能被m/d整除
所以可以得到除法模运算的公式:

  • (a * c) / c ≡ (b * c) / c (mod m/gcd(c,m))

二、求解线性同余方程:

首先引入逆元的概念:

首先考虑实数状况下求解方程ax = b,因为a存在倒数a-1,所以在方程两边同时乘上a-1可以解出x = b * a-1

现在对于线性同余方程ax ≡ b (mod m),如果有满足ay ≡ 1 (mod m) 这样的和实数情况下a的倒数一样的数y存在,就能很快利用y = a-1代入到原方程中来求解线性同余方程,我们把这样的数y叫做a的逆元,求出了逆元之后,就有x = a-1·ax = a-1·b,这样就可以求出x:x = a-1b(mod m)

逆元的求法&逆元和线性同余方程解的关系:

现在我们要如何求出这个线性同余方程的逆元呢?对于方程ax ≡ 1 (mod m),其实等价于:存在整数k使得ax = 1 + mk,稍加变形之后就变成了ax - mk = 1,对于这样一个方程,可以用扩展欧几里德算法求解(不知道的先复习一下,否则后面可能会看不懂),显然当gcd(a,m)!=1时,不存在逆元,因为gcd(a,m)!=1时ax ≡ 1 (mod m)无解
当不存在逆元的情况下,我们可以对原线性同余方程做一下变化:
对于原线性同余方程:ax ≡ b (mod m)
其中a和m不互素(即存在gcd(a,m)!=1)
那么 ax ≡ b (mod m) 就等价于 a/gcd(a,m)·x ≡ b/gcd(a,m) (mod m/gcd(a,m))
简单证明一下:
我们先对a和b同时除以gcd(a,m),根据模运算除法法则,m要除以gcd(m,gcd(a,m)),而gcd(m,gcd(a,m)) = gcd(a,m),所以上面两式等价。
但是为什么要除以gcd(a,m)呢?是因为要使gcd((a/gcd(a,m)),(m/gcd(a,m))) = 1
这样就可以找出a/gcd(a,m)的逆元了

b%gcd(a,m)!=0时,线性同余方程就无解

  • [可以得出结论:当线性同余方程不存在逆元并且b%gcd(a,m)!=0时必定无解]

当有解存在的情况下,gcd((a/gcd(a,m)),(m/gcd(a,m))必然等于1,即对于新的线性同余方程必然存在逆元,则有:x ≡ (a/gcd(a,m))-1 · (b/gcd(a,m)) (mod m/gcd(a,m))
因此!!!! ax ≡ b(mod m)的解为:
x = (a/gcd(a,m))-1 · (b/gcd(a,m)) + k · (m/gcd(a,m))
x ≡ (a/gcd(a,m))-1 · (b/gcd(a,m)) + (m/gcd(a,m)) · k (mod m) ,(k∈[0,gcd(a,m)])
ps:超出范围就是一个循环,m/gcd(a,m)·gcd(a,m) mod m = 0

  • [存在逆元的情况下,一定有多个解]

也可以用费马小定理来求出逆元:
费马小定理(Fermat’s little theorem)是数论中的一个重要定理,在1636年提出,其内容为: 假如p是质数,且gcd(a,p)=1,那么 a(p-1) ≡ 1(mod p),例如:假如a是整数,p是质数,则a,p显然互质(即两者只有一个公约数1),那么我们可以得到费马小定理的一个特例,即当p为质数时候, a(p-1)≡1(mod p)。[From:百度百科]
把费马小定理的式子进行变形可以得到:a-1 ≡ ap-2 (mod p),当p为质数时,用快速幂就能得到逆元a-1

当p是合数的时候,费马小定理就不适用了,这时可以用欧拉函数和欧拉定理来求值

三、求解线性同余方程组

上面讲到了怎么求解线性同余方程,接下来就是求解线性同余方程组的问题了
线性同余方程组就是求解:ai · x ≡ bi (mod mi),(i ∈[1,n]) 的解集
假设我们已经求得了前几个方程组的解集为x ≡ b1 (mod m1) ①
下一个线性同余方程为a2 · x ≡ b2 (mod m2) ②
我们把①式形式转变一下,变成:x = b1 + t · m1 ③
把③式代入②式中,得到a2 · (b1 + t · m1) ≡ b2 (mod m2) ④
把④式移一下项,得到a2 · m1 · t ≡ b2 - a2 · b1 (mod m2) ⑤

如果 (b2 - a2 · b1) % gcd(a2·m1,m2) != 0,那么该方程组就无解
现在我们讨论有解的情况下:
利用之前在线性同余方程的求解中的公式,我们可以得到⑤式的解:
简化一下,令d = gcd(a2·m1,m2)否则就是一坨乱七八糟的东西
得:t ≡ (a2 · m1 / d)-1 · (b2 - a2 · b1) / d (mod m2 / d)
即:t = (a2 · m1 / d)-1 · (b2 - a2 · b1) / d + k · (m2 / d) ⑥
把⑥带回到③式中,得到
x = b1 + ((a2 · m1 / d)-1 · (b2 - a2 · b1) / d + k · (m2 / d)) · m1
再化成①式的形式之后就是:
x ≡ b1 + (((a2 · m1 / d)-1 · (b2 - a2 · b1) / d) · m1) (mod m1 · (m2 / d))
用相同的方式对后面的方程逐个求解就能得到解集了

基本代码:

struct P{                                   //用来存放线性同余方程组的三个参数
    int a,b,m;
}p[maxn];   
int gcd(int a,int b){
    return b ? gcd(b,a%b) : a;
}
void extgcd(int a,int b,int &x,int &y){		//扩展欧几里得算法
    if(b==0){
        x = 1;
        y = 0;
        return;
    }
    extgcd(b,a%b,x,y);
    int x1 = x,y1 = y;
    x = y1;
    y = x1 - a/b * y1;
}
int mod_inverse(int a,int m){			//计算逆元
    int x,y;
    extgcd(a,m,x,y);
    return (m+x%m)%m;
}                                
pair<int,int> linear_congruence(){
    int B = 0,M = 1;                        //最开始解集x ≡ 0 (mod 1)表示全体整数
    for(int i=1;i<=n;i++){
        int a=p[i].a*M, b=p[i].b-p[i].a*B, m=p[i].m, d=gcd(a,m);
        if(b%d!=0) return make_pair(0,-1);//无解
        B = B + (b/d)*mod_inverse(a/d,m/d)*M;
        M = M * p[i].m/d;
    }
    return make_pair(B%M,M);
}

四、中国剩余定理

中国剩余定理主要讲的是如果M可以表示为n*m
那么X ≡ b (mod M)等价于X ≡ b (mod n)也等价于X ≡ b (mod m)
简单证明一下:
X = b + k·M = b + k·n·m
所以:
X = b + (k·n)·m ==> X ≡ b (mod m)
X = b + (k·m)·n ==> X ≡ b (mod n)

当线性同余方程组满足一定条件的情况下我们可以用中国剩余定理来求解问题。
对于线性方程组
X ≡ a1 (mod m1)
X ≡ a2 (mod m2)
X ≡ a3 (mod m3)

X ≡ ar (mod mr)

在m1,m2,…,mr两两互质的情况下就可以用中国剩余定理了
M = m1 * m2 * m3 * … * mr
此时X存在模M的唯一解
设Mi = M/mi,则:
Mi·Mi-1·bi ≡ bi (mod mi)
Mi·Mi-1·bj ≡ 0 (mod mj) ,(j != i) //因为Mi%mj == 0
所以原方程的解可以写成X = a1·M1·M1-1+a2·M1·M1-1+a3·M3·M3-1 … +ar·Mr·Mr-1

int China(){
    int M = 1;
    ans = 0;
    for(int i=1;i<=n;i++) M*=m[i];
    for(int i=1;i<=n;i++){
        int tm = M/m[i];
        ans+=tm*mod_inverse(tm,m[i])*b[i]%M;
    }
    return ans;
}

你可能感兴趣的:(数论)