对于两个整数 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) 。根据定义不难得出以下两个结论:
值得一提的是, 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
由于 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) 。