欧几里得算法(Euclidean algorithm)的证明与实现
欧几里得算法也称为辗转相除法, 用于计算两个正整数的最大公因数, 一般用(a,b)来表示a和b的最大公因数, 计算机领域一般表示成gcd(a,b).
首先,我们给出欧几里得算法依赖的一些基本概念的定义:
定义(整除) 如果a和b为整数且a=0,我们说a整除b是指存在整数c使得b=a⋅c。如果a整除b,我们还称a是b的一个因子,且称b是a的倍数。
如果a整除b,则将其记为a∣b,如果a不能整除b,则记其为a∤b。
定义(最大公因子) 不全为零的整数a和b的最大公因子是指能够同时整除a和b的最大整数。
a和b的最大公因子记作(a,b)。有时也记作gcd(a,b)。
注意当n为正整数时,(0,n)=(n,0)=n。
虽然所有的正整数都能够整除0,我们还是定义(0,0)=0。这样可以确保关于最大公因子的相关结论在所有情况下均成立。
然后我们给出欧几里得算法的定理描述和证明过程
欧几里得算法的定理描述如下:
定理(欧几里得算法) 令整数r0=a,r1=b满足a≥b>0,
如果连续做带余除法得到rj=rj+1⋅qj+1+rj+2,
且0<rj+2<rj+1(j=0,1,2,⋯,n−2),rn+1=0,
那么(a,b)=rn,它是最后一个非零余数。
从定理中我们看到通过连续应用带余除法,在每一步中被除数和除数被更小的数代替(这些更小的数实际上是每一步中的除数和余数),运算直到余数为零时终止。
这一系列运算产生了一系列的等式,而最大公因子就是最后一个非零的余数。
接下来我们看看欧几里得算法的定理证明:
证明 令r0=a,r1=b是正整数且满足a≥b,那么通过连续运用带余除法,我们求得
r0r1rjrn−3rn−2rn−1=r1⋅q1+r2=r2⋅q2+r3⋮=rj+1⋅qj+1+rj+2⋮=rn−2⋅qn−2+rn−1=rn−1⋅qn−1+rn=rn⋅qn0≤r2<r10≤r3<r20≤rj+2<rj+10≤rn−1<rn−20≤rn<rn−1
可以假设最终一定会有一个余数为零,这是因为余数组成的序列a=r0≥r1>r2>⋯≥0所包含的项的个数不会大于a(因为每个余数都是整数)。
由引理1,我们得到(a,b)=(r0,r1)=(r1,r2)=(r2,r3)=⋯=(rn−2,rn−1)=(rn−1,rn)=(rn,0)=rn。因此(a,b)=rn,
这是最后一个非零余数。■
引理1 如果e和d是整数且e=d⋅q+r,其中q,r是整数,那么(e,d)=(d,r)。
证明 在定理1中,取a=r,b=d,c=q,(即(r+q⋅d,d)=(r,d)),那么由定理1可以直接得到引理。■
定理1 令a,b,c是整数,那么(a+c⋅b,b)=(a,b)。
证明 令a,b,c是整数。我们将证明a,b的公因子与a+c⋅b,b的公因子相同,即证明(a+c⋅b,b)=(a,b)。
令e是a,b的公因子。由定理2可知e∣(a+c⋅b),所以e是a+c⋅b和b的公因子。
如果f是a+c⋅b和b的公因子,那么由定理2可知f整除(a+c⋅b)−c⋅b=a,所以f是a,b的公因子。
因此(a+c⋅b,b)=(a,b)。■
定理2 如果a,b,m和n为整数,且c∣a,c∣b,则c∣(m⋅a+n⋅b)。
证明 因为c∣a且c∣b,故存在整数e和f,使得a=c⋅e,b=c⋅f。
因此m⋅a+n⋅b=m⋅c⋅e+n⋅c⋅f=c(m⋅e+n⋅f)。从而,c∣(m⋅a+n⋅b)。■
我们举下面的例子来说明欧几里得算法的具体步骤。
例子1 用欧几里得算法求(252,198)的步骤如下:
2521985436=198×1+54=54×3+36=36×1+18=18×2
我们将这些步骤总结在下表中:
| j | rj | rj+1 | qj+1 | rj+2 |
|---|
| 0 | 252 | 198 | 1 | 54 |
| 1 | 198 | 54 | 3 | 36 |
| 2 | 54 | 36 | 1 | 18 |
| 3 | 36 | 18 | 2 | 0 |
最后一个非零除数(在最后一列倒数第二行的那个数)就是252和198的最大公因子。因此(252,198)=18。◀
最后,我们给出欧几里得算法的python语言实现:
def Euclid(a:int, b:int):
assert a >= b >= 0
if b == 0:
return a
return Euclid(b, a%b)
def gcd(a:int, b:int):
assert a >= 0 and b >= 0
if a < b:
a, b = b, a
return Euclid(a, b)
def print_gcd(a, b):
print("gcd({}, {}) = {}".format(a, b, gcd(a, b)))
if __name__ == "__main__":
print_gcd(252, 198)
def Euclid(a:int, b:int):
assert a >= b >= 0
r = a % b
while r != 0:
a = b
b = r
r = a % b
return b
def gcd(a:int, b:int):
assert a >= 0 and b >= 0
if a < b:
a, b = b, a
return Euclid(a, b)
def print_gcd(a, b):
print("gcd({}, {}) = {}".format(a, b, gcd(a, b)))
if __name__ == "__main__":
print_gcd(252, 198)
另外,python标准库的math模块内置了gcd的实现,可以作为基准版本:
import math
def print_gcd(a, b):
print("gcd({}, {}) = {}".format(a, b, math.gcd(a, b)))
if __name__ == "__main__":
print_gcd(252, 198)
参考文献:
- 初等数论及其应用(原书第6版): ISBN 978-7-111-48697-8
- 离散数学及其应用(原书第8版): ISBN 978-7-111-63687-8
- 算法概论: ISBN 978-7-302-17939-9