扩展欧几里得算法
前置知识:裴蜀定理
裴蜀定理
\qquad 若 a , b a,b a,b 是整数,且 ( a , b ) = d (a,b)=d (a,b)=d,那么对于任意的整数 x , y , a x + b y x,y,ax+by x,y,ax+by 都一定是 d d d 的倍数。特别地,一定存在整数 x , y x,y x,y,使 a x + b y = d ax+by=d ax+by=d 成立,(x,y不唯一)。
\qquad 若 x 0 , y 0 x_0,y_0 x0,y0 满足 a x + b y = d ax+by=d ax+by=d,则满足 { x = x 0 − b d ⋅ k y = y 0 + a d ⋅ k , k ∈ Z \begin{cases}x=x_0-\frac{b}{d}\cdot k \\ y=y_0+\frac{a}{d}\cdot k\end{cases},k\in \mathbb Z {x=x0−db⋅ky=y0+da⋅k,k∈Z,的 x x x 和 y y y 也均满足上式。且这些是 x , y x,y x,y 所有可能的解。
作用
\qquad 扩展欧几里得算法就是用来求解这个特殊的 x , y x,y x,y.
推导证明
a m o d b = a − ⌊ a b ⌋ ⋅ b d = ( a , b ) = ( b , a m o d b ) = ( a − ⌊ a b ⌋ ⋅ b ) x + b y d = a x + b ( y − ⌊ a b ⌋ x ) a\;mod\;b=a-\lfloor \frac{a}{b} \rfloor \cdot b \\ d=(a,b)=(b,a\;mod\;b)=\left(a-\lfloor \frac{a}{b} \rfloor \cdot b\right)x+by \\ d=ax+b\left(y-\lfloor \frac{a}{b} \rfloor x\right) amodb=a−⌊ba⌋⋅bd=(a,b)=(b,amodb)=(a−⌊ba⌋⋅b)x+byd=ax+b(y−⌊ba⌋x)
\qquad
这里的
y
−
⌊
a
b
⌋
x
y-\lfloor \frac{a}{b} \rfloor x
y−⌊ba⌋x 就是下面代码第9行 y -= a / b * x;
的由来。
代码
#include <iostream>
using namespace std;
int exgcd(int a, int b, int &x, int &y) { //以传址的方式返回两个参数
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int a, b, x, y;
scanf("%d%d", &a, &b);
exgcd(a, b, x, y);
printf("%d %d\n", x, y);
return 0;
}
应用
1.求逆元
前置知识:逆元
\qquad 若整数 b b b, m m m 互质,并且对于任意的整数 a a a,如果满足 b ∣ a b|a b∣a,则存在一个整数 x x x,使得 a b ≡ a × x ( m o d m ) \frac{a}{b}\equiv a\times x(mod\space m) ba≡a×x(mod m),则称 x x x 为 b b b 的模 m m m 乘法逆元,记为 b − 1 ( m o d m ) b^{−1}(mod\space m) b−1(mod m)。
也就是说,求得一个 x x x,使得 b ⋅ x ≡ 1 ( m o d m ) b\cdot x\equiv 1(mod\space m) b⋅x≡1(mod m) 成立。
思路
\qquad
这个问题可以看作求一元线性同余方程的一种特殊情况。
∃
x
∈
Z
,
使得
a
x
≡
1
(
m
o
d
m
)
成立
⇕
∃
x
,
y
∈
Z
,
使得
a
x
=
m
y
+
1
成立
⇕
令
y
′
=
−
y
,
∃
x
,
y
′
∈
Z
,
使得
a
x
+
m
y
′
=
1
成立
\exist x\in \mathbb{Z},使得\;ax\equiv 1(mod \; m)\;成立 \\ \Updownarrow \\ \exist x,y\in \mathbb{Z},使得\;ax=my+1 \;成立 \\ \Updownarrow \\ 令y'=-y,\; \exist x,y'\in \mathbb{Z},使得\;ax+my'=1 \;成立
∃x∈Z,使得ax≡1(modm)成立⇕∃x,y∈Z,使得ax=my+1成立⇕令y′=−y,∃x,y′∈Z,使得ax+my′=1成立
\qquad
根据裴蜀定理可知,当且仅当
(
a
,
m
)
=
1
(a,m)=1
(a,m)=1,即
a
a
a 与
m
m
m 互质时,此线性同余方程有解,即
a
a
a 有存在逆元。
\qquad 利用扩展欧几里得算法求得的 x x x 即为 a a a 的逆元。
代码
#include <iostream>
using namespace std;
int exgcd(int a, int b, int &x, int &y) { //以传址的方式返回两个参数
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int a, m, x, y;
scanf("%d%d", &a, &m);
int d = exgcd(a, m, x, y);
if (d == 1)
printf("%d\n", x < 0 ? x + m : x); //为了防止出现负数,等价于:printf("%d\n", (x + m) % m);
else
puts("no exist");
return 0;
}
2.求解线性同余方程
例题
\qquad
给定
n
n
n 组数据
a
i
,
b
i
,
m
i
a_i,b_i,m_i
ai,bi,mi,对于每组数求出一个
x
x
x ,使其满足
a
i
∗
x
i
≡
b
i
(
m
o
d
m
i
)
a_i*x_i\equiv b_i(mod \; m_i)
ai∗xi≡bi(modmi),如果无解,则输出 impossible
.
数据范围
\qquad 1 ≤ n ≤ 1 0 5 , 1 ≤ a i , b i , m i ≤ 2 × 1 0 9 1 \le n \le 10^5,\quad1\le a_i,b_i,m_i\le 2\times 10^9 1≤n≤105,1≤ai,bi,mi≤2×109
输入样例
2
2 3 6
4 3 5
输出样例
impossible
2
思路
\qquad
这就是上文求逆元方法的扩展和延申。
∃
x
∈
Z
,
使得
a
x
≡
b
(
m
o
d
m
)
成立
⇕
∃
x
,
y
∈
Z
,
使得
a
x
=
m
y
+
b
成立
⇕
令
y
′
=
−
y
,
∃
x
,
y
′
∈
Z
,
使得
a
x
+
m
y
′
=
b
成立
\exist x\in \mathbb{Z},使得\;ax\equiv b(mod \; m)\;成立 \\ \Updownarrow \\ \exist x,y\in \mathbb{Z},使得\;ax=my+b \;成立 \\ \Updownarrow \\ 令y'=-y,\; \exist x,y'\in \mathbb{Z},使得\;ax+my'=b \;成立
∃x∈Z,使得ax≡b(modm)成立⇕∃x,y∈Z,使得ax=my+b成立⇕令y′=−y,∃x,y′∈Z,使得ax+my′=b成立
\qquad
根据裴蜀定理可知,记
(
a
,
m
)
=
d
(a,m)=d
(a,m)=d 当且仅当
d
∣
b
d\;|\;b
d∣b 时,此线性同余方程有解。
\qquad 要求 x x x ,则需用扩展欧几里得算法算出满足 a x 0 + m y 0 = d ax_0+my_0=d ax0+my0=d 式子的 x 0 x_0 x0,可得 x = x 0 ⋅ b d x=x_0\cdot\frac{b}{d} x=x0⋅db .
举个例子:
要解 4 x ≡ 3 ( m o d 5 ) 4x\equiv 3(mod\;5) 4x≡3(mod5) 这一方程:
- 先判断 ( 4 , 5 ) = 1 (4,5)=1 (4,5)=1 是 3 3 3 的因数,说明有解;
- 再找出满足 4 x 0 + 5 y 0 = 1 4x_0+5y_0=1 4x0+5y0=1 的一组解 x 0 = − 1 , y 0 = 1 x_0=-1,\;y_0=1 x0=−1,y0=1.
- 最后计算 x x x: − 1 × 3 1 = − 3 -1\times\frac{3}{1}=-3 −1×13=−3,因此解为 x ≡ − 3 ( m o d 5 ) x\equiv-3(mod\;5) x≡−3(mod5).
代码
#include <iostream>
using namespace std;
typedef long long LL;
int exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int n;
scanf("%d", &n);
while (n--) {
int a, b, m, x, y;
scanf("%d%d%d", &a, &b, &m);
int d = exgcd(a, m, x, y);
if (b % d)
puts("impossible");
else{
LL res = (LL)x * b / d % m;
printf("%lld\n", res < 0 ? res + m : res);
}
}
return 0;
}
3.求解线性同余方程组
前置知识:中国剩余定理
\qquad 中国剩余定理又称孙子定理或中国余数定理,是数论中的一个关于一元线性同余方程组的定理,说明了一元线性同余方程组有解的准则以及求解方法。
经典题目表述
有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?
\qquad 大意是:一个整数除以三余二,除以五余三,除以七余二,求这个整数。
\qquad
用现代数学的语言来说明,题目为:给出以下一元线性同余方程组,求满足下列同余式的整数
x
x
x.
{
x
≡
a
1
(
m
o
d
m
1
)
x
≡
a
2
(
m
o
d
m
2
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
\begin{cases} x\equiv a_1(mod\;m_1) \\ x\equiv a_2(mod\;m_2) \\ \vdots \\ x\equiv a_n(mod\;m_n) \\ \end{cases}
⎩
⎨
⎧x≡a1(modm1)x≡a2(modm2)⋮x≡an(modmn)
其中,
m
1
,
m
2
,
…
m
n
m_1,m_2,\dots m_n
m1,m2,…mn 两两互质。
注意:这里两两互质是指任意两个数均互质,而不是这些数的最大公因数为1.
例如:{6,8,9} 这三个数的最大公因数为1,但是他们并非两两互质。
解题方法
设
M
=
∏
i
=
1
n
m
i
,
M
i
=
M
m
i
用
M
i
−
1
表示
M
i
在模
m
i
意义下的逆元,则解为:
x
≡
∑
i
=
1
n
a
i
M
i
M
i
−
1
(
m
o
d
M
)
设M=\prod\limits_{i=1}^{n}m_i,\quad M_i=\frac{M}{m_i} \\ 用M_i^{-1}表示M_i在模m_i意义下的逆元,则解为: \\ x\equiv \sum\limits_{i=1}^{n}a_iM_iM_i^{-1}\;(mod\;M)
设M=i=1∏nmi,Mi=miM用Mi−1表示Mi在模mi意义下的逆元,则解为:x≡i=1∑naiMiMi−1(modM)
\qquad
在此处需要用扩展欧几里得定理计算一个数字的逆元。由此我们就可以知道,这种方法需要
m
1
,
m
2
,
…
m
n
m_1,m_2,\dots m_n
m1,m2,…mn 两两互质的原因就在于需要求每个
M
i
M_i
Mi 在模
m
i
m_i
mi 下的逆元(逆元存在的充要条件就是
M
i
M_i
Mi 和
m
i
m_i
mi 互质)。
举个例子:
{
x
≡
2
(
m
o
d
3
)
x
≡
3
(
m
o
d
5
)
x
≡
2
(
m
o
d
7
)
\begin{cases} x\equiv 2(mod\;3) \\ x\equiv 3(mod\;5) \\ x\equiv 2(mod\;7) \\ \end{cases}
⎩
⎨
⎧x≡2(mod3)x≡3(mod5)x≡2(mod7)
就拿“物不知数”这个经典题举例:
-
首先判断 3 , 5 , 7 3,5,7 3,5,7 之间确实两两互质,可以使用中国剩余定理;
-
求出 M = 3 × 5 × 7 = 105 M=3\times5\times7=105 M=3×5×7=105;
-
分别计算 M i M_i Mi 和 M i − 1 M_i^{-1} Mi−1 :
M 1 = 5 × 7 = 35 , M 1 − 1 = 2 − 1 ( m o d 3 ) = 2 ; M 2 = 3 × 7 = 21 , M 2 − 1 = 1 − 1 ( m o d 5 ) = 1 ; M 3 = 3 × 5 = 15 , M 3 − 1 = 1 − 1 ( m o d 7 ) = 1 ; M_1=5\times7=35,\;M_1^{-1}=2^{-1}(mod\;3)=2; \\ M_2=3\times7=21,\;M_2^{-1}=1^{-1}(mod\;5)=1; \\ M_3=3\times5=15,\;M_3^{-1}=1^{-1}(mod\;7)=1; \\ M1=5×7=35,M1−1=2−1(mod3)=2;M2=3×7=21,M2−1=1−1(mod5)=1;M3=3×5=15,M3−1=1−1(mod7)=1; -
求解 x x x.
x = a 1 M 1 M 1 − 1 + a 2 M 2 M 2 − 1 + a 3 M 3 M 3 − 1 = 2 × 35 × 2 + 3 × 21 × 1 + 2 × 15 × 1 = 233 因此, x ≡ 23 ( m o d 105 ) x=a_1M_1M_1^{-1}+a_2M_2M_2^{-1}+a_3M_3M_3^{-1} \\ =2\times35\times2+3\times21\times1+2\times15\times1 \\ =233\\ 因此,x\equiv 23 (mod\;105) x=a1M1M1−1+a2M2M2−1+a3M3M3−1=2×35×2+3×21×1+2×15×1=233因此,x≡23(mod105)
最小的正整数解为 23 23 23 。
注意:
\qquad 这个定理给出了在 m 1 , m 2 , … m n m_1,m_2,\dots m_n m1,m2,…mn 两两互质这种特殊情况下的构造解,而并不是一元线性同余方程组的通解。
\qquad 我们可以验证这个定理的正确性:
\qquad
只需验证
对于
∀
i
∈
{
x
∈
N
∗
∣
1
≤
x
≤
n
}
,
均满足
∑
j
=
1
n
a
j
M
j
M
j
−
1
≡
a
i
(
m
o
d
m
i
)
对于\forall i \in \{x\in \mathbb{N}^*|1\le x \le n\},均满足\sum\limits_{j=1}^{n}a_jM_jM_j^{-1} \equiv a_i(mod\;m_i)
对于∀i∈{x∈N∗∣1≤x≤n},均满足j=1∑najMjMj−1≡ai(modmi)
\qquad
计算
∑
j
=
1
n
a
j
M
j
M
j
−
1
\sum\limits_{j=1}^{n}a_jM_jM_j^{-1}
j=1∑najMjMj−1 的值时,可以看作这个式子中
n
n
n 项分别取模再相加。对于其中某一项:
- 当 j ≠ i j\ne i j=i 时,其 M j M_j Mj 中包含因数 m i m_i mi,因此该项对 m i m_i mi 取模得 0 0 0.
- 当 j = i j= i j=i 时,其 M j ⋅ M j − 1 M_j\cdot M_j^{-1} Mj⋅Mj−1 对 m i m_i mi 取模得 1 1 1,故实际为 a i a_i ai 对 m i m_i mi 取模。
综上所述,
∑
j
=
1
n
a
j
M
j
M
j
−
1
(
m
o
d
m
i
)
=
a
i
+
0
+
0
+
⋯
+
0
=
a
i
\sum\limits_{j=1}^{n}a_jM_jM_j^{-1}(mod\;m_i)=a_i+0+0+\cdots+0=a_i
j=1∑najMjMj−1(modmi)=ai+0+0+⋯+0=ai
代码:
#include <iostream>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y) { //扩展欧几里得算法
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
LL M = 1, m1[30], m2[30];
LL a[30], m[30], res;
int main() {
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%lld%lld", a + i, m + i);
M *= m[i];
}
for (int i = 1; i <= n; i++) {
m1[i] = M / m[i];
LL x, y;
LL d = exgcd(m1[i], m[i], x, y);
if (d == 1) {
m2[i] = x < 0 ? x + m[i] : x;
} else { //说明不满足两两互质
puts("error!");
return 0;
}
res += a[i] * m1[i] * m2[i] % M;
}
res %= M;
printf("%lld", res < 0 ? res + M : res);
return 0;
}
例题
204. 表达整数的奇怪方式 - AcWing题库(略微改动)
\qquad 给定 2 n 2n 2n 个整数 a 1 , a 2 , … , a n a_1,a_2,\dots,a_n a1,a2,…,an 和 m 1 , m 2 , … , m n m_1,m_2,\dots,m_n m1,m2,…,mn,求一个最小的非负整数 x x x,满足 ∀ i ∈ [ 1 , n ] , x ≡ a i ( m o d m i ) \forall i \in [1,n],x\equiv a_i(mod\;m_i) ∀i∈[1,n],x≡ai(modmi).
数据范围
\qquad 1 ≤ a i ≤ 2 31 − 1 , 1\le a_i\le2^{31}-1, 1≤ai≤231−1,
\qquad 0 ≤ a i < m i 0\le a_i <m_i 0≤ai<mi
\qquad 1 ≤ n ≤ 25 1\le n\le 25 1≤n≤25
输入样例
2
7 8 //a1和m1
9 11 //a2和m2
输出样例
31
思路
\qquad 此题没有指明 a 1 , a 2 , … a n a_1,a_2,\dots a_n a1,a2,…an 两两互质,因此不能盲目使用中国剩余定理。
\qquad
采用另一种通解,步骤如下:
{
x
≡
a
1
(
m
o
d
m
1
)
(
1
)
x
≡
a
2
(
m
o
d
m
2
)
(
2
)
⋮
x
≡
a
n
(
m
o
d
m
n
)
\begin{cases} x\equiv a_1(mod\;m_1)\qquad(1) \\ x\equiv a_2(mod\;m_2)\qquad(2) \\ \vdots \\ x\equiv a_n(mod\;m_n) \\ \end{cases}
⎩
⎨
⎧x≡a1(modm1)(1)x≡a2(modm2)(2)⋮x≡an(modmn)
\qquad
先研究(1)(2)式组成的方程组,可以写作:
x
=
k
1
m
1
+
a
1
(
k
1
∈
Z
)
=
k
2
m
2
+
a
2
(
k
2
∈
Z
)
x=k_1m_1+a_1\;(k_1\in\Bbb{Z}) \\ \quad=k_2m_2+a_2\;(k_2\in\Bbb{Z})
x=k1m1+a1(k1∈Z)=k2m2+a2(k2∈Z)
\qquad
变形为:
k
1
m
1
−
k
2
m
2
=
a
2
−
a
1
(
3
)
k_1m_1-k_2m_2=a_2-a_1\qquad\qquad(3)
k1m1−k2m2=a2−a1(3)
\qquad
根据裴蜀定理,要使
k
1
,
k
2
k_1,k_2
k1,k2 有解,即
(
m
1
,
m
2
)
∣
(
a
2
−
a
1
)
(m_1,m_2)|(a_2-a_1)
(m1,m2)∣(a2−a1).
\qquad
记
d
=
(
m
1
,
m
2
)
d=(m_1,m_2)
d=(m1,m2) ,利用扩展欧几里得算法可以得到
k
1
m
1
−
k
2
m
2
=
d
k_1m_1-k_2m_2=d
k1m1−k2m2=d 的一组解,记作
k
1
′
,
k
2
′
k_1',k_2'
k1′,k2′。将其扩大至成为(3)式的解:
{
k
10
=
k
1
′
⋅
a
2
−
a
1
d
k
20
=
k
2
′
⋅
a
2
−
a
1
d
\begin{cases} k_{10}=k_1'\cdot \frac{a_2-a_1}{d} \\ k_{20}=k_2'\cdot \frac{a_2-a_1}{d} \end{cases}
{k10=k1′⋅da2−a1k20=k2′⋅da2−a1
\qquad
那么所有的解为
{
k
1
=
k
10
+
m
2
d
⋅
p
k
2
=
k
20
+
m
1
d
⋅
p
,
p
∈
Z
\begin{cases}k_1=k_{10}+\frac{m_2}{d}\cdot p \\ k_2=k_{20}+\frac{m_1}{d}\cdot p\end{cases},p\in \mathbb Z
{k1=k10+dm2⋅pk2=k20+dm1⋅p,p∈Z,则
x
=
k
1
m
1
+
a
1
=
(
k
10
+
m
2
d
⋅
p
)
m
1
+
a
1
=
m
1
k
10
+
a
1
+
p
⋅
m
1
m
2
d
=
m
1
k
10
+
a
1
+
p
⋅
[
m
1
,
m
2
]
x=k_1m_1+a_1\qquad\qquad\qquad\; \\ =\left(k_{10}+\frac{m_2}{d}\cdot p\right)m_1+a_1\\ =m_1k_{10}+a_1+p\cdot\frac{m_1m_2}{d} \\ =m_1k_{10}+a_1+p\cdot[m_1,m_2]
x=k1m1+a1=(k10+dm2⋅p)m1+a1=m1k10+a1+p⋅dm1m2=m1k10+a1+p⋅[m1,m2]
\qquad
中括号表示两个数的最小公倍数。
\qquad
这样来看,我们把两个式子融合成了一个形式相同的式子:
令
M
=
[
m
1
,
m
2
]
,
A
=
m
1
k
10
+
a
1
则
:
x
=
p
⋅
M
+
A
即
:
x
≡
A
(
m
o
d
M
)
令M=[m_1,m_2],\;A=m_1k_{10}+a_1 \\ 则: x=p\cdot M+A\\ 即: x\equiv A(mod\;M)
令M=[m1,m2],A=m1k10+a1则:x=p⋅M+A即:x≡A(modM)
代码
#include <iostream>
using namespace std;
typedef long long LL;
LL exgcd(LL a, LL b, LL &x, LL &y) { //扩展欧几里得算法
if (!b) {
x = 1, y = 0;
return a;
}
LL d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
int main() {
int n;
scanf("%d", &n);
bool flag = true; //是否有解
LL a1, m1;
scanf("%lld%lld", &a1, &m1);
for (int i = 1; i < n; i++) {
LL a2, m2;
scanf("%lld%lld", &a2, &m2);
LL k1, k2;
LL d = exgcd(m1, m2, k1, k2);
if ((a2 - a1) % d) {
flag = false; //无解
break;
}
k1 *= (a2 - a1) / d;
LL t = m2 / d;
k1 = (k1 % t + t) % t; //更新k1为符合要求的最小正整数
a1 = m1 * k1 + a1; //转化成一个新的式子
m1 = m1 / d * m2;
}
if (flag)
printf("%lld\n", (a1 % m1 + m1) % m1);
else
puts("-1");
return 0;
}