辗转相除法与拓展欧几里得算法

基础知识

对于两个整数 x , y x,y x,y ,若 ∃ k ∈ Z \exist k\in\mathbb{Z} kZ 使得 x k = y xk=y xk=y ,则称 x x x 整除 y y y ,记作 x ∣ y x\mid y xy

对于两个整数 a , b a,b a,b ,若整数 c c c 同时满足 c ∣ a c\mid a ca c ∣ b c\mid b cb ,则称 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) 。根据定义不难得出以下两个结论:

  1. d d d a , b a,b a,b 的公因数,则 d ∣ gcd ⁡ ( a , b ) d\mid \gcd(a,b) dgcd(a,b)
  2. 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=0b=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 dm 。裴蜀等式有解时必然有无穷多个解。

证明:

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 bm ,又 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+byx,yZ},设 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 rqN+,0r<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=pqd0=ax1+by1q(ax0+by0)=a(x1qx0)+b(y1qy0)A 由于 d 0 d_0 d0 A A A 中最小正元素,而 0 ⩽ r < d 0 0\leqslant r0r<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,pAN+

由于 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 d0p,pA 。特别的, d 0 ∣ a , d 0 ∣ b d_0\mid a,d_0\mid b d0a,d0b ,因此 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,tZ ,那么 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} dd0 。所以 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, m0y0kd0a)kZ} 其中 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 mA ,因此 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 db d ∣ r = a − b q d\mid r=a-bq dr=abq ,因此 d ∣ d 1 d\mid d_1 dd1
另一方面,因为有 d 1 ∣ b d_1\mid b d1b d 1 ∣ a = ( a − b q ) + b q d_1\mid a=(a-bq)+bq d1a=(abq)+bq ,因此 d 1 ∣ d d_1\mid d d1d
d 1 ∣ d d_1\mid d d1d d ∣ d 1 d\mid d_1 dd1 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,yZ 是方程 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+(abba)yay+b(xbay) 因此 x ′ = y , y ′ = x − ⌊ a b ⌋ y {\displaystyle x'=y,y'=x-\lfloor\frac{a}{b}\rfloor y} x=y,y=xbay 即为方程 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)

你可能感兴趣的:(算法笔记,算法,ACM,C/C++)