最大公因数(GCD)

欧几里得算法(辗转相除法)

在证明辗转相除法的正确性之前,我们需要列出下面几个定理:

  1. 如果两数有一个公共因子 d,那么 d 一定是这两数相加/相减的结果的因子。jd + kd = (j+k)d
  2. 任意两个大于零的整数 ab 满足 a\ge b,都可以写成 a = b * q + r,其中 qr 是非负整数,q 称为 ab 的商,r 称为余数。
  3. 上式可变形为 r = a - b * q,取 q 为最大值,易知 r<b
    • 如果 r 不为 0,b*(q + 1) - a = b - r 可得,ab 的公因子一定是 b*(q+1)a 的公因子,也就是 br 的公因子。又有 r + b * (q + 1) = a + brb 的公因子也一定是 ab 的公因子。a 与 b 的公因子一定是 b 与 r 的公因子,b 与 r 的公因子一定是 a 与 b 的公因子,因此 \{x|x = a 与 b 的公因子\} = \{x|x = b 与 r 的公因子\}
    • 如果 r 为 0,即 ba 的因子,那么 ab 的最大公因子就是 b

因此,如果 r\ne 0,则有公式:

\text{gcd}(r, b) = \text{gcd}(b, a)

因此可以得到辗转相除的过程:

  1. r = a \% b
  2. r 如果等于零,输出 b
  3. 否则 a = b,b = r,回到第一步

例:
22 与 8 的最大公因数:

  1. 22 \% 8 = 6 \ne 0
  2. 8 \% 6 = 2 \ne 0
  3. 6 \% 2 = 0,输出 2

上面的过程对辗转相减法同样适用,边界条件是差为 0,输出减数。

更相减损法(二进制 GCD 算法)

是辗转相除/辗转相减的改进版,利用移位操作提高效率,适合计算机实现。

  1. 如果 a=0,返回 b,如果 b=0,返回 a。(边界条件:差为 0)
  2. 如果 a、b 都是偶数,返回 2*gcd(a/2, b/2)
  3. 如果 a 是偶数,返回 gcd(a/2,b)
  4. 如果 b 是偶数,返回 gcd(a,b/2)
  5. 如果都是奇数,a>b 则返回 gcd(a-b,b),否则返回 gcd(a,b-a)

由于除 2 操作可以用移位实现,该算法通常比传统的辗转相除法快 20%~50%,而且适合超大数进行运算。
奇数 - 奇数必定是偶数,因此情况 5 一定不会连续出现,因此算法总体复杂度为 O(logN)。

最小公倍数(LCM)

用最大公因数求解

lcm 与 gcd 之间存在公式:

\mathrm{lcm}(a, b) = \frac{|a \times b|}{\mathrm{gcd}(a, b)}

要想证明这个公式,需要依赖整数的唯一质分解定理

a = p_1^{e_1} \times p_2^{e_2} \times \dots \times p_k^{e_k} \\ b = p_1^{f_1} \times p_2^{f_2} \times \dots \times p_k^{f_k}

其中 p_1,p_2... 为质数。a、b 的最大公因数其实就是:

\mathrm{gcd}(a, b) = p_1^{\min(e_1, f_1)} \times p_2^{\min(e_2, f_2)} \times \dots \times p_k^{\min(e_k, f_k)}

最小公倍数则是:

\mathrm{lcm}(a, b) = p_1^{\max(e_1, f_1)} \times p_2^{\max(e_2, f_2)} \times \dots \times p_k^{\max(e_k, f_k)}

然后,显然:

\max(e_i, f_i) + \min(e_i, f_i) = e_i + f_i

综合上面的表达式,得到:

a \times b = p_1^{e_1 + f_1} \times p_2^{e_2 + f_2} \times \dots \times p_k^{e_k + f_k}\\ = \mathrm{gcd}(a, b) \times \mathrm{lcm}(a, b)

求多个整数的 LCM

根据 LCM 的性质,可以得到:

\mathrm{LCM}(a_1, a_2, \dots, a_n) = \mathrm{LCM}(\mathrm{LCM}(a_1, a_2, \dots, a_{n-1}), a_n)