前言
之前写过一篇关于求素数算法的博文,其中的算法主要是判断出某个数是不是一个素数。本文就来探讨一下有关素数算法的另一个问题——求最大公约数。本文的内容,来自于笔者最近看的两本书里面提及的算法,一本是《计算机是怎样跑起来的》,还有一本是《什么是数学 · 对思想和方法的基本研究》,下面就用代码来实现一遍书中的算法。
更相减损法
更相减损法出自中国古代的数学专著,其原文是
可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。
—— 《九章算术》
这段文言文翻译成白话文,包含了下面四个算法步骤
-
如果两个数都是偶数,那就先将它们除以二
-
若两个数都是奇数,那么就比较两者大小,用大的数字作被减数,小的数字作减数
-
将减法得出的结果与减数作比较,重复上面的步骤
-
直到最后两个数相等,那么最大公约数就是最后相等的那个数,或最后相等的数乘以每次约去的2最后的乘积
C语言实现
int gcd(int x, int y)
{
// 任意两个自然数都能同时被1整除
int gcdNum = 1;
while (x != y) {
// 如果两个数同时能被2整除,那么都先除以2
if (x % 2 == 0 && y % 2 == 0) {
x /= 2;
y /= 2;
gcdNum *= 2;
}
else {
if (x > y)
x -= y;
else
y -= x;
}
}
return gcdNum * x;
}
上述的代码,其实并没有什么优化之处。只要稍作思考就知道,由奇数 - 偶数 = 奇数、偶数 - 奇数 = 奇数这一简单算术规律可知,一旦 x 与 y 其中一个不是偶数了,那么接下来的出现的所有数对都是一奇一偶的形式了,所以也就没有必要每次都去判断 x 和 y 是否同时满足偶数这一条件。
再加上更相减损法中,执行最多的操作是步骤2的减法操作,计算机对于这类计算可以计算地非常快,所以算法可以直接使用一个步骤——减法实现
Python代码实现
def gcd(x, y):
while x != y:
if x > y:
x -= y
else:
y -= x
else:
return x
欧几里得辗转相除法
两千多年前,欧几里得在其著作《几何原本》中提出了辗转相除法,当时没有那么现代化的数学语言描述,所以他当时用的是几何线段来演示这个算法。
欧几里得的做法是:取两根不同长度的线段,视作两个不同的整数,不断地用其中较短的那条线段,去尽可能地截取较长的那条线段,直到较长的线段被截取成比原来较短线段更短,然后用新的较短的线段去尽可能截取较长的线段,如此重复这一过程,直到较短的线段正好将较长线段截取成整数倍,此时较短的线段长度,就是两者的最大公约数了。
我在维基百科上找到了一张描述这一过程的动态图,很生动直观地体现了这一过程
关于欧几里得辗转相除法的代数证明如下
- 前提一:如果 a a a 是任一整数,而 b b b 是任一大于 0 0 0 的整数,那么总能找到一个整数 q q q ,使得等式: a = b q + r a = bq + r a=bq+r 恒成立,其中的整数 r r r 满足: 0 ≤ r < b 0 \le r < b 0≤r<b
- 前提二:如果整数