先看百度百科对欧几里得算法的介绍:
欧几里德算法又称辗转相除法,是指用于计算两个正整数a,b的最大公约数。
计算公式:gcd(a,b) = gcd(b,a mod b)。
反复计算上式,当算式所得余数为0时,该算式的除数就为a,b两个数的最大公因数。
看到上面这个公式,我们很自然想到用递归解决这个问题。代码如下:
# dividend——被除数,divisor——除数
def gcd(dividend, divisor):
if 0 == divisor:
return dividend
dividend, divisor = divisor, dividend % divisor
return gcd(dividend, divisor)
或者这样写:
# dividend——被除数,divisor——除数
def gcd(dividend, divisor):
if 0 == divisor:
return dividend
return gcd(divisor, dividend % divisor)
当然,也可以用循环解决。代码如下:
# dividend——被除数,divisor——除数
def gcd(dividend, divisor):
while 0 != divisor:
dividend, divisor = divisor, dividend % divisor
return dividend
讨论一下时间复杂度:
不妨令被除数(a)大于除数(b),即:
a > b ; a , b ≠ 0 a > b;a,b \ne 0 a>b;a,b̸=0
(1)当 b 等于 1 时:
a ÷ b = a ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0 a \div b = a\cdot \cdot\cdot\cdot\cdot\cdot0 a÷b=a⋅⋅⋅⋅⋅⋅0
a,b的最大公因数为 b,即:1
时间复杂度为O(1)
(2)当 b 大于 1 时:
a ÷ b = c ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ d a \div b = c\cdot \cdot\cdot\cdot\cdot\cdot d a÷b=c⋅⋅⋅⋅⋅⋅d
因为 d > 0 d >0 d>0,所以 c < b / 2 c < b / 2 c<b/2 恒成立
即:a每次的值会减少一半以上
时间复杂度为O(logn)
由此可得,欧几里得算法的时间复杂度为O(logn)。
同样先看百度百科对扩展欧几里得算法的介绍:
扩展欧几里德算法是用来在已知a, b求解一组x,y,使它们满足贝祖等式:
aX + bY = gcd(a, b) = d(解一定存在,根据数论中的相关定理)。
这个算式不太好理解,因此不知道该如何写代码。我们先对这个式子进行演算。
演算步骤如下:
不妨令被除数(a)大于除数(b),即:
a > b ; a , b ≠ 0 a > b;a,b \ne 0 a>b;a,b̸=0
设:
a X 1 + b Y 1 = g c d ( a , b ) a{X}_1 + b{Y}_1 = gcd(a, b) aX1+bY1=gcd(a,b)
又设:
b X 2 + ( a % b ) Y 2 = g c d ( b , ( a % b ) ) b{X}_2 + (a \% b){Y}_2 = gcd(b,(a \% b)) bX2+(a%b)Y2=gcd(b,(a%b))
由欧几里得定理:
g c d ( a , b ) = g c d ( b , ( a % b ) ) gcd(a, b) = gcd(b, (a \% b)) gcd(a,b)=gcd(b,(a%b))
可得:
a X 1 + b Y 1 = b X 2 + ( a % b ) Y 2 a{X}_1 + b{Y}_1 = b{X}_2 + (a \% b){Y}_2 aX1+bY1=bX2+(a%b)Y2
又因为在数学中:
a % b = a − [ a / b ] ∗ b a \% b = a - [a / b] * b a%b=a−[a/b]∗b
则:
a X 1 + b Y 1 = b X 2 + ( a − [ a / b ] ∗ b ) Y 2 = b X 2 + a Y 2 − [ a / b ] ∗ b Y 2 a{X}_1 + b{Y}_1 = b{X}_2 + (a - [a / b] * b){Y}_2 = b{X}_2 + a{Y}_2 - [a / b] * b{Y}_2 aX1+bY1=bX2+(a−[a/b]∗b)Y2=bX2+aY2−[a/b]∗bY2
化简得:
a X 1 + b Y 1 = a Y 2 + b ( X 2 − [ a / b ] Y 2 ) a{X}_1 + b{Y}_1 = a{Y}_2 + b ({X}_2 - [a/b]{Y}_2) aX1+bY1=aY2+b(X2−[a/b]Y2)
由恒等式可得:
X 1 = Y 2 , Y 1 = X 2 − [ a / b ] Y 2 {X}_1 = {Y}_2, {Y}_1 = {X}_2 - [a/b]{Y}_2 X1=Y2,Y1=X2−[a/b]Y2
由于gcd的不断递归,[a/b] 最终必然会等于 0 。
基于上式,我们便可以用递归解决这个问题了。代码如下:
import math
# dividend——被除数,divisor——除数,remainder——余数
def ex_gcd(dividend, divisor):
if 0 == divisor:
# 易证明:当除数等于0时,可得 X = 1, Y = 0。假设最大公因数为被除数。
# 注意:数学上,0和自然数没有最大公因数,此处是为了方便理解。
return 1, 0, dividend
# 得到 X2 和 Y2 的值,并且将求最大公因数的步骤混入其中了
x2, y2, remainder = ex_gcd(divisor, dividend % divisor)
# 得到 X1 和 Y1 的值
temp = x2
x1 = y2
y1 = temp - int(math.floor(dividend / divisor)) * y2
return x1, y1, remainder
由于主要算法还是欧几里得算法,因此时间复杂度仍然为O(logn)。
如有错误,欢迎指正。感谢各位的阅览。