基础知识
对于两个整数 x , y x,y x,y ,若 ∃ k ∈ Z \exist k\in\mathbb{Z} ∃k∈Z 使得 x k = y xk=y xk=y ,则称 x x x 整除 y y y ,记作 x ∣ y x\mid y x∣y 。
对于两个整数 a , b a,b a,b ,若整数 c c c 同时满足 c ∣ a c\mid a c∣a 和 c ∣ b c\mid b c∣b ,则称 c c c 是 a a a 和 b b b 的公因数, max { c } \max\{c\} max{c} 称为 a , b a,b a,b 的最大公因数,记作 gcd ( a , b ) \gcd(a,b) gcd(a,b) 。根据定义不难得出以下两个结论:
- 若 d d d 是 a , b a,b a,b 的公因数,则 d ∣ gcd ( a , b ) d\mid \gcd(a,b) d∣gcd(a,b) 。
- gcd \gcd gcd 运算满足交换律和结合律。
值得一提的是, gcd ( 0 , 0 ) \gcd(0,0) gcd(0,0) 是不存在的,当然我们也可以人为定义 gcd ( 0 , 0 ) = 0 \gcd(0,0)=0 gcd(0,0)=0 。为不引起冗余的讨论,以下所有涉及 gcd ( a , b ) \gcd(a,b) gcd(a,b) 的场景都有 a ≠ 0 ∨ b ≠ 0 a\ne0\vee b\ne 0 a=0∨b=0 。
( B e ˊ zout \text{Bézout} Beˊzout 定理)对任意两个整数 a , b a,b a,b ,设 d = gcd ( a , b ) d=\gcd(a,b) d=gcd(a,b) 。那么关于未知数 x , y x,y x,y 的线性丢番图方程(称为裴蜀等式) a x + b y = m ax+by=m ax+by=m 有整数解 ( x , y ) (x,y) (x,y) 当且仅当 d ∣ m d\mid m d∣m 。裴蜀等式有解时必然有无穷多个解。
证明:
若 a , b a,b a,b 中有一个为 0 0 0 ,不妨设 a = 0 a=0 a=0 ,则原等式变为 b y = m by=m by=m 。该等式有整数解当且仅当 b ∣ m b\mid m b∣m ,又 gcd ( 0 , b ) = b \gcd(0,b)=b gcd(0,b)=b ,因此结论成立。
下面假设 a , b a,b a,b 都不为 0 0 0 。
设 A = { a x + b y ∣ x , y ∈ Z } A=\{ax+by\vert x,y\in \mathbb {Z}\} A={ax+by∣x,y∈Z},设 d 0 = a x 0 + b y 0 d_{0}=ax_{0}+by_{0} d0=ax0+by0 为 A A A 中最小正元素。考虑 A A A 中任意一个正元素 p = a x 1 + b y 1 p=ax_{1}+by_{1} p=ax1+by1 设 p p p 关于 d 0 d_0 d0 的带余除法为 p = q d 0 + r p=qd_{0}+r p=qd0+r 其中 q ∈ N + , 0 ⩽ r < d 0 q\in\mathbb{N}^{+},0\leqslant r<d_{0} q∈N+,0⩽r<d0。则 r = p − q d 0 = a x 1 + b y 1 − q ( a x 0 + b y 0 ) = a ( x 1 − q x 0 ) + b ( y 1 − q y 0 ) ∈ A \begin{aligned}r&=p-qd_{0}\\&=ax_{1}+by_{1}-q(ax_{0}+by_{0})\\&=a(x_1-qx_0)+b(y_1-qy_0)\in A\end{aligned} r=p−qd0=ax1+by1−q(ax0+by0)=a(x1−qx0)+b(y1−qy0)∈A 由于 d 0 d_0 d0 是 A A A 中最小正元素,而 0 ⩽ r < d 0 0\leqslant r<d_{0} 0⩽r<d0 ,因此 r = 0 r=0 r=0 。因此 d 0 ∣ p , ∀ p ∈ A ∩ N + d_{0}\ |\ p,\forall p \in A\cap\mathbb{N}^{+} d0 ∣ p,∀p∈A∩N+ 。
由于 A A A 中正负数可以建立一一对应(将 p p p 中的 x 1 , y 1 x_1,y_1 x1,y1 分别取反即可),因此 d 0 ∣ p , ∀ p ∈ A d_0\mid p,\forall p \in A d0∣p,∀p∈A 。特别的, d 0 ∣ a , d 0 ∣ b d_0\mid a,d_0\mid b d0∣a,d0∣b ,因此 d 0 d_{0} d0 是 a a a 和 b b b 的公约数。
另一方面,对 a a a 和 b b b 的任意正公约数 d d d ,设 a = k d , b = t d a=kd,b=td a=kd,b=td ,其中 k , t ∈ Z k,t\in\mathbb{Z} k,t∈Z ,那么 d 0 = a x 0 + b y 0 = d ( k x 0 + t y 0 ) d_{0}=ax_{0}+by_{0}=d(kx_{0}+ty_{0}) d0=ax0+by0=d(kx0+ty0) 因此 d ∣ d 0 d\mid d_{0} d∣d0 。所以 d 0 = gcd ( a , b ) d_{0}=\gcd(a,b) d0=gcd(a,b) 。
在方程 a x + b y = m ax+by=m ax+by=m 中,如果 m = m 0 d 0 m=m_{0}d_{0} m=m0d0 ,那么方程显然有无穷多个解: { ( m 0 x 0 + k b d 0 , m 0 y 0 − k a d 0 ) ∣ k ∈ Z } \left\{\left(m_{0}x_{0}+{k\frac{b}{d_0}},\ m_{0}y_{0}-{k\frac{a}{d_0}}\right)\mid k\in \mathbb {Z} \right\} {(m0x0+kd0b, m0y0−kd0a)∣k∈Z} 其中 x 0 x_0 x0 和 y 0 y_0 y0 通常称为裴蜀等式的一组特解。
相反的,如果 a x + b y = m ax+by=m ax+by=m 有整数解,那么 m ∈ A m\in A m∈A ,因此 d 0 ∣ m d_{0}\ |\ m d0 ∣ m 。证毕。
辗转相除法
该算法用来求解两整数 a , b a,b a,b 的最大公因数,由欧几里得发明,所以也叫欧几里得算法。原理如下:
首先假设
a
=
b
q
+
r
a=bq+r
a=bq+r 和
d
=
gcd
(
a
,
b
)
,
d
1
=
gcd
(
b
,
r
)
d=\gcd(a,b),d_1=\gcd(b,r)
d=gcd(a,b),d1=gcd(b,r) 。
一方面,因为有
d
∣
b
d\mid b
d∣b 和
d
∣
r
=
a
−
b
q
d\mid r=a-bq
d∣r=a−bq ,因此
d
∣
d
1
d\mid d_1
d∣d1 ;
另一方面,因为有
d
1
∣
b
d_1\mid b
d1∣b 和
d
1
∣
a
=
(
a
−
b
q
)
+
b
q
d_1\mid a=(a-bq)+bq
d1∣a=(a−bq)+bq ,因此
d
1
∣
d
d_1\mid d
d1∣d 。
由
d
1
∣
d
d_1\mid d
d1∣d 和
d
∣
d
1
d\mid d_1
d∣d1 得
d
=
d
1
d=d_1
d=d1 ,即
gcd
(
a
,
b
)
=
gcd
(
b
,
a
m
o
d
b
)
\gcd(a,b)=\gcd(b,a\bmod b)
gcd(a,b)=gcd(b,amodb) 利用这个结论,我们可以通过不断取模缩小
a
a
a 和
b
b
b 直到有一个数为
0
0
0。
C++的STL中定义了用辗转相除法求最大公因数的函数,函数名称__gcd
,所在文件algorithm
,源码如下:
template<typename _EuclideanRingElement>
_EuclideanRingElement
__gcd(_EuclideanRingElement __m, _EuclideanRingElement __n){
while (__n != 0){
_EuclideanRingElement __t = __m % __n;
__m = __n;
__n = __t;
}
return __m;
}
我们也可以定义自己的gcd
函数:
int gcd(int a, int b){return b ? gcd(b, a % b) : a;}
拓展欧几里得算法
该算法用来求不定方程 a x + b y = m ax+by=m ax+by=m 的一组特解。原理就是利用 B e ˊ zout \text{Bézout} Beˊzout 定理和辗转相除法的思想:
首先,方程 a x + b y = m ax+by=m ax+by=m 有解的充要条件为 gcd ( a , b ) ∣ m \gcd(a,b)\mid m gcd(a,b)∣m ,因此我们可以先求解方程 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的解,再将解放大得到原解。
其次,假设 x , y ∈ Z x,y\in\mathbb{Z} x,y∈Z 是方程 b x + ( a m o d b ) y = gcd ( b , a m o d b ) bx+(a\bmod b)y=\gcd(b,a\bmod b) bx+(amodb)y=gcd(b,amodb) 的一组解,则 gcd ( a , b ) = gcd ( b , a m o d b ) = b x + ( a m o d b ) y = b x + ( a − b ⌊ a b ⌋ ) y = a y + b ( x − ⌊ a b ⌋ y ) \begin{aligned}&\gcd(a,b)\\=&\gcd(b,a\bmod b)\\=&bx+(a\bmod b)y\\=&bx+(a-b\lfloor\frac{a}{b}\rfloor)y\\=&ay+b(x-\lfloor\frac{a}{b}\rfloor y)\end{aligned} ====gcd(a,b)gcd(b,amodb)bx+(amodb)ybx+(a−b⌊ba⌋)yay+b(x−⌊ba⌋y) 因此 x ′ = y , y ′ = x − ⌊ a b ⌋ y {\displaystyle x'=y,y'=x-\lfloor\frac{a}{b}\rfloor y} x′=y,y′=x−⌊ba⌋y 即为方程 a x ′ + b y ′ = gcd ( a , b ) ax'+by'=\gcd(a,b) ax′+by′=gcd(a,b) 的一组解。
利用这个结论,我们就可以像辗转相除法那样不断缩小 a , b a,b a,b 来求原等式的解。因此该算法被认为是欧几里得算法的拓展,称为拓展欧几里得算法。
我们可以这样实现该算法:
int exgcd(int a, int b, int &x, int &y){
if (b == 0) return x = 1, y = 0, a;
int d = exgcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
上述函数求出方程 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的一组特解 x 0 , y 0 x_0,y_0 x0,y0 ,并返回 gcd ( a , b ) \gcd(a,b) gcd(a,b) 。