文章目录
1 欧几里得算法(Euclidean algorithm)
1.1 概述
欧几里得算法,又称辗转相除法,是用于求两个不全等于0的非负整数的最大公约数的算法。
一般用 g c d ( a , b ) gcd(a,b) gcd(a,b) 表示 a a a 与 b b b 的最大公约数,其中 gcd 是 Greatest Common Divisor 的缩写。
欧几里得算法的核心思想可以归结为一个简短而又优雅的式子—— g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a,b)=gcd(b,a \bmod b) gcd(a,b)=gcd(b,amodb)。(默认 a ≥ b a \ge b a≥b)
通过这样的一个式子,我们可以把原问题的规模不断缩小,即一直递归直到到达 base case (即 b = 0 b=0 b=0 )为止。当 b = 0 b=0 b=0 时显然有 g c d ( a , 0 ) = a gcd(a,0)=a gcd(a,0)=a,此时 base case 中的 a a a 值即为原问题中的 a a a 与 b b b 的最大公约数。
下面证明为何 g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a,b)=gcd(b,a \bmod b) gcd(a,b)=gcd(b,amodb) 成立。
1.2 正确性证明
设 g 0 = g c d ( a , b ) g_0=gcd(a,b) g0=gcd(a,b),则 g 0 ∣ a g_0 \mid a g0∣a, g 0 ∣ b g_0 \mid b g0∣b。
设 g 1 = g c d ( b , a m o d b ) g_1=gcd(b,a \bmod b) g1=gcd(b,amodb),则 g 1 ∣ b g_1 \mid b g1∣b, g 1 ∣ ( a m o d b ) g_1 \mid (a \bmod b) g1∣(amodb)。
因为 a = ⌊ a / b ⌋ b + ( a m o d b ) a= \lfloor a/b \rfloor b+(a \bmod b) a=⌊a/b⌋b+(amodb),而 g 1 g_1 g1 同时整除 b b b 和 ( a m o d b ) (a\bmod b) (amodb),故有 g 1 ∣ a g_1 \mid a g1∣a。
由 g 1 ∣ a g_1 \mid a g1∣a 和 g 1 ∣ b g_1 \mid b g1∣b 以及 g 0 g_0 g0 是 a a a 和 b b b 的最大公约数的事实,可得知 g 1 ∣ g 0 g_1 \mid g_0 g1∣g0。
因为 a m o d b = a − ⌊ a / b ⌋ b a \bmod b=a-\lfloor a/b \rfloor b amodb=a−⌊a/b⌋b,而 g 0 g_0 g0 同时整除 a a a 和 b b b,故有 g 0 ∣ ( a m o d b ) g_0 \mid (a \bmod b) g0∣(amodb)。
由 g 0 ∣ b g_0 \mid b g0∣b 和 g 0 ∣ ( a m o d b ) g_0 \mid (a \bmod b) g0∣(amodb) 以及 g 1 g_1 g1 是 b b b 和 ( a m o d b ) (a \bmod b) (amodb) 的最大公约数的事实,可得知 g 0 ∣ g 1 g_0 \mid g_1 g0∣g1。
因为 g 1 ∣ g 0 g_1 \mid g_0 g1∣g0 及 g 0 ∣ g 1 g_0 \mid g_1 g0∣g1,故有 g 0 = g 1 g_0=g_1 g0=g1。
证毕。
1.3 复杂度分析
随便 google 一下就能找到不少借助 Fibonacci 数列得出复杂度为 O ( log n ) \mathcal{O}(\log n) O(logn) 的帖子。
这里介绍另一种稍微容易一点的分析方法。
设 r k r_k rk 为进行了 k k k 次取模后的余数,并用 r 0 r_0 r0 表示 b b b,则有:
a m o d r 0 = r 1 r 0 m o d r 1 = r 2 ⋮ r k m o d r k + 1 = r k + 2 ⋮ a \bmod r_0=r_1\\r_0 \bmod r_1=r_2\\\vdots\\r_k \bmod r_{k+1}=r_{k+2}\\\vdots amodr0=r1r0modr1=r2⋮rkmodrk+1=rk+2⋮
观察若干步骤后会发现 —— r k + 2 ≤ 1 / 2 r k r_{k+2} \le 1/2r_k rk+2≤1/2rk,即递归过程每加深两层,问题规模必小于等于原来的一半。
下面给出证明。
- 若 r k + 1 ≥ 1 / 2 r k r_{k+1} \ge 1/2r_k rk+1≥1/2rk,则有 r k − r k + 1 ≤ 1 / 2 r k r_k-r_{k+1} \le 1/2r_k rk−rk+1≤1/2rk,故 r k + 2 = r k m o d r k + 1 ≤ r k − r k + 1 ≤ 1 / 2 r k r_{k+2}=r_k \bmod r_{k+1} \le r_k-r_{k+1} \le 1/2r_k rk+2=rkmodrk+1≤rk−rk+1≤1/2rk。
- 若 r k + 1 < 1 / 2 r k r_{k+1} < 1/2r_k rk+1<1/2rk,则有 r k + 2 = r k m o d r k + 1 < r k + 1 ≤ 1 / 2 r k r_{k+2}=r_k \bmod r_{k+1}<r_{k+1} \le 1/2r_k rk+2=rkmodrk+1<rk+1≤1/2rk。
综上,必有 r k + 2 ≤ r k r_{k+2} \le r_k rk+2≤rk。
证毕。
故 Euclidean algorithm 的时间复杂度为 O ( log n ) \mathcal{O}(\log n) O(logn)。
1.4 代码
LL gcd(LL a, LL b)
{
if (b > a) swap(a, b);
if (b == 0) return a;
return gcd(b, a % b);
}
2 扩展欧几里得算法(extended Euclidean algorithm)
2.1 概述
扩展欧几里得算法实际上就是在欧几里得算法的基础上给出了如何构造 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b) 的解的方法,其中 a a a 与 b b b 为不全等于0的两个非负整数, x x x 和 y y y 为任意整数。
2.2 算法流程及相关证明
2.2.1 解的存在性证明
下面使用构造法来证明解的存在性
-
当 b = 0 b=0 b=0 时,显然有 g c d ( a , b ) = a gcd(a,b)=a gcd(a,b)=a,故一组解为 x = 1 , y = 0 x=1,y=0 x=1,y=0。
-
当 b ≠ 0 b \ne 0 b=0 时,设 a x 0 + b y 0 = g c d ( a , b ) ax_0+by_0=gcd(a,b) ax0+by0=gcd(a,b), b x 1 + ( a m o d b ) y 1 = g c d ( b , a m o d b ) bx_1+(a \bmod b)y_1=gcd(b,a \bmod b) bx1+(amodb)y1=gcd(b,amodb)。
由 g c d ( a , b ) = g c d ( b , a m o d b ) gcd(a,b)=gcd(b,a \bmod b) gcd(a,b)=gcd(b,amodb) 可得 a x 0 + b y 0 = b x 1 + ( a m o d b ) y 1 ax_0+by_0=bx_1+(a \bmod b)y_1 ax