欧几里得算法/扩展欧几里得算法的Python实现

欧几里得算法/扩展欧几里得算法的Python实现

  • 欧几里得算法的实现
  • 扩展欧几里得算法的实现

欧几里得算法的实现

先看百度百科对欧几里得算法的介绍:

欧几里德算法又称辗转相除法,是指用于计算两个正整数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>bab̸=0
(1)当 b 等于 1 时:
a ÷ b = a ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ 0 a \div b = a\cdot \cdot\cdot\cdot\cdot\cdot0 a÷b=a0
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=cd
因为 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>bab̸=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)

如有错误,欢迎指正。感谢各位的阅览。

你可能感兴趣的:(欧几里得算法/扩展欧几里得算法的Python实现)