from:2017 CCF计算机课程改革导教班. 陈道蓄
12
欧几里得算法
本章将讨论的算法是古代文献中出现的最古来的算法之一。欧几里得在大约公元前300年撰写的几何原理一书中描述了此算法。今天这个算法是计算机科学多个领域的基石。正如本书第16章将介绍的,特别在密码学领域,许多程序依赖于我们是否能够高效的计算两个数的最大公约数。
设想一下你有两根细木棒,长度分别为a和b,假设a,b均为整数。你要把他们截成小段,每段长度为相等的整数,并希望每段尽可能长。我们当然可以将长度为a的棒截为a个长度为1的短棒,将长度为b的棒截为b个长度为1的短棒。但有可能使每个短棒更长些吗?
我们的算法要计算截断的最大公共长度。下面描述算法的两个版本,第一个运行较慢,或者说效率较差;而第二个运行较快,效率较高。
假设d表示可能得到的最大长度。长度为a,b的棒分别被截为a/d和b/d段。在图12.1显示的例子中长度为a的棒被截为5段,长度为b的棒被截为三段。
图12.1 将两根细木棒截断为长度同为d的短棒
如果两根木棒一样长,即a=b,d的值立即可知,根本不需要截断。木棒本身的长度就是可能的最大长度d。因此我们下面可以假设a,b不相等,不妨设a大于b。如果我们将两根木棒像图12.2所示挨着放置,一端对齐,就会有一个重要的发现:假如我们能将两根棒截断为长度均为d的短棒,我们可以先从较长的棒一端截下长度为b的一段。
图12.2 长度为a,b的木棒可以截得的最大等长也就是
长度为a-b和b的木棒可以截得的最大等长
剩下的木棒长度是a-b,它同样可以截为长度为d的若干短棒。反之,如果我们能将这一段长度为a-b的木棒与长度为b的木棒都截断为最大长度为d的短棒,当然也就可以将长度为a的木棒截断为若干长度为d的短棒。
我们将上面的观察总结为更精确的表述,就成为我们的算法背后的主要原理。
原理(P)
如果a=b,则需要的d就等于a。
如果a>b, 则对于a,b最大截断长度就等于对于a-b和b的最大截断长度。
现在我们可以给出计算对于a,b的最大截断长度d的算法。
最大等长段的截断长度
当两个棒剩余长度仍不相等便执行如下操作:
从较长的棒上截下与较短的棒等长的一段放到一边去。
上述操作不再执行意味着当前两个棒长度相等了。它们的长度就是我们要找的长度。
现在我们必须回答为什么上面的算法能得到结果,用计算机科学的说法,为什么算法会“终止”。它确实会终止。记住开始时两个棒的长度a,b均为整数。因此每次从长的棒上截下与短棒等长的一段,剩余的长度仍然是整数。具体说两个棒剩余长度不会小于1。注意每一次截断截下来的长度至少是1。由此可知算法最多执行a+b轮操作。
最大公约数
以上分析说明上面计算出的长度d也一定是整数。这个整数既能整除a,也能整除b。采用数学表述方式,我们说存在整数x,y,满足a=x×d且b=y×d。d是使得上述x,y存在的最大整数。D成为a和b的最大公约数。而x,y是能够分别将长度为a,b的棒截断为长度均为d的段的数量。
我们也可以不再提棒,用更抽象的描述讨论上述算法。算法的输入时两个正整数a,b。算法的输出是a和b的最大公约数。我们将这个算法称为“慢欧几里得”算法,后面我们将解释为什么这样称它。
图12.3 算法执行的一步
慢欧几里得
While a¹b
If a>b, then 用a-b替代a
If b>a, then 用b-a替代b
输出a,b的共同值
现在看一个小例子。
假设输入时15和9。第1步,从15中减去9得到新的两个数6=15-9和9。第2步我们得到6和3。第3步得到3和3,算法输出3。
下一个例子说明为什么这个算法称为“慢欧几里得”。假如输入a=1001, b=2。算法执行过程先后得到的中间结果将会是
1001和2
999和2
997和2
995和2
…… (许多轮中间结果)
3和2
1和2
1和1
算法需要的执行时间很长是因为输入的第二个数比第一个数小的太多。
如何能提高算法的速度
在上面的算法中从1001中减去2需要做多少次呢?1001=2×500+1。所以要做500次减法才能减到结果小于2。
计算机执行带余数的除法效率很高。对于任意的正整数a和b计算机计算另外两个整数q和r,使得a=q×b+r。其中不小于0,但严格小于b。在上面的例子中a=1001, b=2, q=500, r=1。
假如a,b是慢欧几里得算法的输入,且a>b,如果b不能整除a,那么算法会执行q次减b的操作,并得到不小于1的余数r。假如b能整除a,那执行q-1次减b的操作将使得两个棒的长度相等。这意味着我们可以直接用余数r替代原来的a,而不必做多次减法。最后会导致余数为0,此时b就是我们要找的最大公约数,算法就此终止。
这就是下面的欧几里得算法的基本思想。
欧几里得算法
1 If a<b 交换a和b;
2 While b>0
3 计算整数q, r, 满足 a=q×b+r, 其中0£r<b;
4 a:=b; b:=r;
5 输出a
分析
你很可能会想欧几里得算法比慢欧几里得算法快很多。为了证实这个猜想,让我们严格地分析一下算法中循环执行的次数。假设a>b。在算法第1步中替代a的r会有多大呢?下面的图告诉我们余数r最大不超过a/2。因为a至少是b+r,但b>r,所以a>2×r。
图12.4 余数r的大小
于是,在第1轮中替代b的数最大不超过a/2。而在第2轮中替代a的数最大也不超过a/2。换句话说,两轮后两个数均不超过a/2。
如果我们联系执行2×k轮循环,那么两个数的值都不会超过a/2k。当k>log2a,两个值均为0。这中情况其实不会发生,在那之前算法必然会终止。因此算法中循环的执行次数上限为2×log2a,这里仍然采用以2为底的对数。
在十进制中表示一个数a需要的位数与log2a成正比。这说明采用慢欧几里得算法,循环次数与a,b本身的值成整部,而采用欧几里得算法,循环次数与a,b的十进制表述的位数成正比。这导致运行时间差别巨大。
一个例子
最后我们用手工计算a=1324和b=145的最大公约数。
第1次带余数的除法是1324=9×145+19。接下来a被置为145,而b则是19.
第2次带余数除法是145=7×19+12。第3次带余数的除法是19=1×12+7。后面的计算分别是12=7+5, 7=5+2, 5=2×2+1, 2=2×1+0,就此我们知道1324和145的最大公约数为1。如果两个整数的最大公约数是1,我们成这两个数“互质”。