前言
上个章节简单介绍了 扩展欧几里得定理,那么这个章节我们就来简述一下如何通过这个定理求解线性同余方程。
一、线性同余方程
线性同余方程(也叫模线性方程)是最基本的同余方程,即
a
x
≡
b
(
m
o
d
n
)
ax \equiv b(mod \ n)
ax≡b(mod n)
其中
a
a
a、
b
b
b、
n
n
n 都为常量,
x
x
x 是未知数,这个方程可以进行一定的转化,得到:
a
x
=
k
n
+
b
ax = kn + b
ax=kn+b
这里的
k
k
k 为任意整数,于是我们可以得到更加一般的形式即:
a
x
+
b
y
+
c
=
0
ax + by + c = 0
ax+by+c=0
这个方程就是二维空间中的直线方程,但是
x
x
x 和
y
y
y 的取值为整数,所以这个方程的解是一些排列成直线的点集。
二、线性同余方程求解
求解同余方程第一步是转化成一般式: a x + b y = c ax + by = c ax+by=c 这个方程的求解步骤如下:
1)求最大公约数
首先求出 a a a 和 b b b 的最大公约数 d = g c d ( a , b ) d = gcd(a, b) d=gcd(a,b),那么原方程可以转化成 d ( a x d + b y d ) = c d(a \frac xd + b \frac yd) = c d(adx+bdy)=c,容易知道 ( a x d + b y d ) (a \frac xd + b \frac yd) (adx+bdy) 为整数,如若 d d d 不能整除 c c c,方程必然无解,算法结束;否则进入 2)。
2)扩展欧几里得算法
由2)可以得知,方程有解则一定可以表示成
a
x
+
b
y
=
c
=
g
c
d
(
a
,
b
)
×
c
′
ax + by = c = gcd(a, b) \times c'
ax+by=c=gcd(a,b)×c′,那么我们先来看如何求解
d
=
g
c
d
(
a
,
b
)
=
a
x
+
b
y
d = gcd(a, b) = ax + by
d=gcd(a,b)=ax+by,根据欧几里德定理,有:
d
=
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
%
b
)
(
1
)
=
b
x
′
+
(
a
%
b
)
y
′
(
2
)
=
b
x
′
+
[
a
−
b
×
⌊
a
b
⌋
]
y
′
(
3
)
=
a
y
′
+
b
[
x
′
−
⌊
a
b
⌋
y
′
]
(
4
)
\begin{aligned} d &= gcd(a, b) \\&= gcd(b, a\%b) & (1)\\&= bx' + (a\%b)y' & (2)\\&= bx' + [a-b \times \lfloor \frac ab \rfloor]y' & (3)\\&= ay' + b[x' - \lfloor \frac ab \rfloor y'] & (4)\end{aligned}
d=gcd(a,b)=gcd(b,a%b)=bx′+(a%b)y′=bx′+[a−b×⌊ba⌋]y′=ay′+b[x′−⌊ba⌋y′](1)(2)(3)(4)
于是有:
{
x
=
y
′
y
=
x
′
−
⌊
a
b
⌋
y
′
\begin{cases}x &= y' \\ y &= x' - \lfloor \frac ab \rfloor y' \end{cases}
{xy=y′=x′−⌊ba⌋y′
由于 g c d ( a , b ) gcd(a, b) gcd(a,b) 是一个递归的计算,所以在求解 ( x , y ) (x, y) (x,y) 时, ( x ′ , y ′ ) (x', y') (x′,y′) 其实已经利用递归计算出来了,递归出口为 b = = 0 b == 0 b==0 的时候(对比辗转相除,也是 b = = 0 b == 0 b==0 的时候递归结束),那么这时方程的解 x 0 = 1 , y 0 = 0 x_0 = 1, y_0 = 0 x0=1,y0=0。
三、线性同余求解源码
// aX + bY = 1
Type ExpGcd(Type a, Type b, Type &X, Type &Y) {
Type q, temp;
if( !b ) {
q = a; X = 1; Y = 0;
return q;
}else {
q = ExpGcd(b, a % b, X, Y);
temp = X;
X = Y;
Y = temp - (a / b) * Y;
return q;
}
}
扩展欧几里德算法 的返回值是
g
c
d
(
a
,
b
)
gcd(a, b)
gcd(a,b),传参加入两个未知数
X
X
X,
Y
Y
Y,采用引用的形式进行传递,对应上文提到的
x
,
y
x, y
x,y,递归出口为
b
=
=
0
b == 0
b==0,这时返回值为当前的
a
a
a,因为
g
c
d
(
a
,
0
)
=
a
gcd(a, 0) = a
gcd(a,0)=a,
(
X
,
Y
)
(X, Y)
(X,Y) 初值为
(
1
,
0
)
(1, 0)
(1,0),然后经过回溯不断计算新的
(
X
,
Y
)
(X, Y)
(X,Y),这个计算是利用了之前的
(
X
,
Y
)
(X, Y)
(X,Y) 进行迭代计算的,直到回溯到最上层算法终止。最后得到的
(
X
,
Y
)
(X, Y)
(X,Y) 就是方程
g
c
d
(
a
,
b
)
=
a
x
+
b
y
gcd(a, b) = ax + by
gcd(a,b)=ax+by 的解。
通过扩展欧几里德求的是
a
x
+
b
y
=
g
c
d
(
a
,
b
)
ax + by = gcd(a, b)
ax+by=gcd(a,b) 的解,令解为
(
x
0
,
y
0
)
(x0, y0)
(x0,y0),代入原方程,得:
a
x
0
+
b
y
0
=
g
c
d
(
a
,
b
)
ax_0 + by_0 = gcd(a, b)
ax0+by0=gcd(a,b)
如果要求
a
x
+
b
y
=
c
=
g
c
d
(
a
,
b
)
×
c
′
ax + by = c = gcd(a, b) \times c'
ax+by=c=gcd(a,b)×c′,可以将上式代入,得:
a
x
+
b
y
=
c
=
(
a
x
0
+
b
y
0
)
c
′
ax + by = c = (ax_0 + by_0)c'
ax+by=c=(ax0+by0)c′
则
x
=
x
0
c
′
,
y
=
y
0
c
′
x = x_0c', y = y_0c'
x=x0c′,y=y0c′,这里的
(
x
,
y
)
(x, y)
(x,y) 只是这个方程的其中一组解,
x
x
x 的通解为
(
x
0
c
′
+
k
b
g
c
d
(
a
,
b
)
∣
k
∈
Z
)
( x_0c' + k \frac b {gcd(a, b)} | k \in Z )
(x0c′+kgcd(a,b)b∣k∈Z),
y
y
y 的通解可以通过
x
x
x 通解的代入得出。
四、例题讲解
有两只青蛙,青蛙 A 和 青蛙 B ,它们在一个首尾相接的数轴上。设青蛙 A 的出发点坐标是 x x x,青蛙 B 的出发点坐标是 y y y 。青蛙A一次能跳 m m m 米,青蛙 B 一次能跳 n n n 米,两只青蛙跳一次所花费的时间相同。数轴总长 L L L 米。要求它们至少跳了几次以后才会碰面。
假设跳了
t
t
t 次后相遇,则可以列出方程:
(
x
+
m
t
)
%
L
=
(
y
+
n
t
)
%
L
(x + mt) \% L = (y + nt) \% L
(x+mt)%L=(y+nt)%L
将未知数
t
t
t 移到等式左边,常数移到等式右边,得到模线性方程:
(
m
−
n
)
t
%
L
=
(
y
−
x
)
%
L
(m-n) t \%L = (y-x) \% L
(m−n)t%L=(y−x)%L
利用扩展欧几里德定理可以求得
t
t
t 的通解
(
t
0
+
k
d
∣
k
∈
Z
)
(t_0 + kd | k \in Z)
(t0+kd∣k∈Z),由于这里需要求
t
t
t 的最小正整数,而
t
0
t_0
t0 不一定是最小的正整数,甚至有可能是负数,我们发现t的通解是关于
d
d
d 同余的,所以最后的解可以做如下处理:
a
n
s
=
(
t
0
%
d
+
d
)
%
d
ans = (t_0 \% d + d) \% d
ans=(t0%d+d)%d
五、思考题
请阐述扩展欧几里得算法这四步的推导过程:
d
=
g
c
d
(
a
,
b
)
=
g
c
d
(
b
,
a
%
b
)
(
1
)
=
b
x
′
+
(
a
%
b
)
y
′
(
2
)
=
b
x
′
+
[
a
−
b
×
⌊
a
b
⌋
]
y
′
(
3
)
=
a
y
′
+
b
[
x
′
−
⌊
a
b
⌋
y
′
]
(
4
)
\begin{aligned} d &= gcd(a, b) \\&= gcd(b, a\%b) & (1)\\&= bx' + (a\%b)y' & (2)\\&= bx' + [a-b \times \lfloor \frac ab \rfloor]y' & (3)\\&= ay' + b[x' - \lfloor \frac ab \rfloor y'] & (4)\end{aligned}
d=gcd(a,b)=gcd(b,a%b)=bx′+(a%b)y′=bx′+[a−b×⌊ba⌋]y′=ay′+b[x′−⌊ba⌋y′](1)(2)(3)(4)