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=y′y=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=m(m为任意整数)
对于这个更一般的方程来说,如果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=k∗x0y=k∗y0
其中,
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
a∗Xi+b∗Yi=ma∗Xj+b∗Yj=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)
a∗Xi+b∗Yi=a∗Xj+b∗Yja(Xi−Xj)=−b(Yi−Yj)
左右两边,同时除以
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(Xi−Xj)=−gcdb(Yi−Yj)
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是(Xi−Xj)的倍数,而且gcda是−(Yi−Yj)的倍数。
可见,任意两个解中的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+gcdb∗kY=y0−gcda∗k
其中
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+gcdb∗k
变形为
x
0
=
X
−
b
g
c
d
∗
k
x_0 = X - \frac{b}{gcd} * k
x0=X−gcdb∗k
观察上式,则求原式的最小整数解的问题就转换为求:
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
ax−my=b
这个负号其实可以算作y本身的负号,上式就变成
a
x
+
m
y
=
b
(
这
个
y
不
是
上
式
的
y
,
多
了
个
负
号
)
ax + my = b(这个y不是上式的y,多了个负号)
ax+my=b(这个y不是上式的y,多了个负号)
到了这一步,只要用扩展欧几里得求出x和y就好了呀!
求逆元
当线性同余方程ax = b(mod m)中,当b = 1,a和m互质时,求出的x就是a的逆元,记作 a − 1 a^-1 a−1。