【算法学习】扩展欧几里得算法详解及C++代码实现

0. 欧几里得算法

欧几里得算法用于求解两个数的最大公约数。代码如下:

int gcd(int a, int b){
    
    
    if(b == 0) return a;
    return gcd(b, a % b);
}

当b为0时,结束递归,此时a即为a和b的最大公约数。

1. 裴蜀定理

定理内容:

如果a、b均是整数,则一定存在整数x和y,使得ax + by = gcd(a, b)成立。

   我们可以再深层次的理解一下式子ax + by = gcd(a, b)的含义,也就是说,整数a和整数b进行线性运算,可以拼凑出他们的最大公约数。再想一想,是不是也可以拼凑出他们最大公约数的整数倍?是的,可以拼凑出他们最大公约数的在整数倍,只要x和y进行对应的放大或缩小就可以了。
    那也就是说,只要等号右侧是gcd(a,b)的整数倍,就一定存在整数x和整数y。(注意这里,后边用扩展欧几里得求解线性同余方程时会用到)
    注意定理中说的是一定存在整数x和整数y,但是并没有告诉我们如何找到x和y,扩展欧几里得算法可以找到整数x和整数y。

2. 扩展欧几里得算法

我们当前想求解的方程是
a x + b y = g c d ( a , b ) (1) ax + by = gcd(a, b) \tag{1} ax+by=gcd(a,b)(1)
如果使用传统的欧几里得算法,递归的下一层应该是
b x ′ + ( a % b ) y ′ = g c d ( b , a % b ) (2) bx' + (a \% b)y' = gcd(b, a \% b) \tag{2} bx+(a%b)y=gcd(b,a%b)(2)
其中
g c d ( b , a % b ) = g c d ( a , b ) gcd(b, a \% b) = gcd(a, b) gcd(b,a%b)=gcd(a,b)
整理一下②式:
b x ′ + ( a − ( a / b ) ∗ b ) y ′ = g c d ( a , b ) a y ′ + b ( x ′ − ( a / b ) ∗ b ) = g c d ( a , b ) (3) bx' + (a -(a / b)*b)y' = gcd(a, b)\\ ay' + b(x' - (a/b)*b) = gcd(a, b) \tag{3} bx+(a(a/b)b)y=gcd(a,b)ay+b(x(a/b)b)=gcd(a,b)(3)
①和③等号右侧相等,所以左侧相等,即:
a x + b y = a y ′ + b ( x ′ − ( a / b ) ∗ y ) (4) ax + by = ay' + b(x' - (a/b)*y)\tag{4} ax+by=ay+b(x(a/b)y)(4)
由④式可得:
x = y ′ y = x ′ − ( a / b ) ∗ y ′ x = y'\\ y=x' - (a/b)*y' x=yy=x(a/b)y
x’和y’是下层的答案,所以,只要知道下层的答案,就可以得到本层的x和y。
那么这样递归的出口在哪里?
出口依然是欧几里得算法的出口,即b为0时,a为a和b的最大公约数。对于方程
a x + b y = g c d ( a , b ) ax + by = gcd(a, b) ax+by=gcd(a,b)
来说,当b为0时,gcd(a, b)就是a。所以此时x = 1,y任意都可以,通常让y等于0。
综上,我们对原来的欧几里得算法代码进行修改:

int exgcd(int a, int b, int &x, int &y){
    
    
    if(b == 0){
    
    
	    x = 1;
	    y = 0;
	    return a;
    }
    int d = exgcd(b, a % b, y, x);
    y -= (a / b) * x;
    return d;
}

这里在递归的时候,x和y参数交叉传递,是为了方便代码书写,可以自己捋顺一下。

3. 扩展欧几里得应用

解二元一次不定方程

上边说到欧几里得可以解决解出裴蜀定理中的x和y,在裴蜀定理中方程为
a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)
等号右侧为a和b的最大公约数,其实这是一种特殊情况,更一般的情况应该是等号右侧是一个任意整数。即
a x + b y = m ( m 为 任 意 整 数 ) ax + by =m(m为任意整数) ax+by=mm
对于这个更一般的方程来说,如果m不是gcd(a,b)的整数倍,则没有整数解,如果m是 g c d ( a , b ) gcd(a,b) gcd(a,b) k k k倍,则有整数解,且解为
x = k ∗ x 0 y = k ∗ y 0 x =k*x_0\\ y = k*y_0\\ x=kx0y=ky0
其中, x 0 x_0 x0 y 0 y_0 y0 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)的结果。
上边扩展欧几里得求出的x和y其实是一组特解。
方程 a x + b y = m ax + by = m ax+by=m的解=通解 + 特解。
特解已经有了,下面对通解进行说明。
现在假设(Xi,Yi)和(Xj, Yj)是不定方程ax + by = m的两组解,则有
a ∗ X i + b ∗ Y i = m a ∗ X j + b ∗ Y j = m a*Xi + b*Yi = m\\ a*Xj + b*Yj = m aXi+bYi=maXj+bYj=m
联立两个方程有
a ∗ X i + b ∗ Y i = a ∗ X j + b ∗ Y j a ( X i − X j ) = − b ( Y i − Y j ) a*Xi + b*Yi = a*Xj + b*Yj\\ a(Xi-Xj) = -b(Yi - Yj) aXi+bYi=aXj+bYja(XiXj)=b(YiYj)
左右两边,同时除以 g c d ( a , b ) gcd(a,b) gcd(a,b)
a g c d ( X i − X j ) = − b g c d ( Y i − Y j ) \frac{a}{gcd}(Xi-Xj) = -\frac{b}{gcd}(Yi-Yj) gcda(XiXj)=gcdb(YiYj)
a和b均除以他们的最大公约数以后的结果互质,所以
b g c d 是 ( X i − X j ) 的 倍 数 , 而 且 a g c d 是 − ( Y i − Y j ) 的 倍 数 。 \frac{b}{gcd}是(Xi-Xj)的倍数,而且\frac{a}{gcd}是-(Yi-Yj) 的倍数。 gcdb(XiXj)gcda(YiYj)
可见,任意两个解中的X之差,一定是 b g c d \frac{b}{gcd} gcdb的倍数,Y同理,所以通解为:
X = x 0 + b g c d ∗ k Y = y 0 − a g c d ∗ k X = x_0 + \frac{b}{gcd} * k\\ Y = y_0 - \frac{a}{gcd} * k X=x0+gcdbkY=y0gcdak
其中 x 0 x_0 x0 y 0 y_0 y0是一组特解。

最小正整数解

以X为例,要求X大于0,且是满足条件的最小的那个X值,即最小正整数解。
通解为
X = x 0 + b g c d ∗ k X = x_0 + \frac{b}{gcd} * k X=x0+gcdbk
变形为
x 0 = X − b g c d ∗ k x_0 = X - \frac{b}{gcd} * k x0=Xgcdbk
观察上式,则求原式的最小整数解的问题就转换为求: x 0 x_0 x0 % b g c d \frac{b}{gcd} gcdb的正整数结果。
如果 x 0 x_0 x0 % b g c d \frac{b}{gcd} gcdb的结果是正数,那就已经是最小正整数解了,如果 x 0 x_0 x0 % b g c d \frac{b}{gcd} gcdb的结果是负数,则再加上一个 b g c d \frac{b}{gcd} gcdb转成正数即是答案。

解线性同余方程

线性同余方程:

ax = b(mod m)
即 ax % m = b,求满足条件的x。

对于这个方程,我们可以转化一下,可以理解为,ax减去m的y倍等于b,即
a x − m y = b ax - my = b axmy=b
这个负号其实可以算作y本身的负号,上式就变成
a x + m y = b ( 这 个 y 不 是 上 式 的 y , 多 了 个 负 号 ) ax + my = b(这个y不是上式的y,多了个负号) ax+my=byy
到了这一步,只要用扩展欧几里得求出x和y就好了呀!

求逆元

当线性同余方程ax = b(mod m)中,当b = 1,a和m互质时,求出的x就是a的逆元,记作 a − 1 a^-1 a1

猜你喜欢

转载自blog.csdn.net/u014117943/article/details/108428551
今日推荐