Skip to content

欧几里得算法

数学中,辗转相除法,又称欧几里得算法(英语:Euclidean algorithm),是求最大公约数算法。辗转相除法首次出现于欧几里得的《几何原本》(第VII卷,命题i和ii)中,而在中国则可以追溯至东汉出现的《九章算术》。

过程

欧几里得算法基于一个非常简单的原理:对于两个数a和b(a > b), a和b的最大公约数与b和a - b的最大公约数相同。

重复的迭代这个过程, 使gcd(a,b)gcd(b,ab). 如此一来, 参数不断减小, 最后某时刻两个参数的值必然相等, 此时a、b的值即为最大公约数.

减运算代码实现

python
def gcd(a, b):
    if b == a: # 或 if b == 0, 因为b == a时再迭代一次后必然是gcd(a, 0)
        return a
    if a < b:
        return gcd(b, a)
    return gcd(a - b, b)

欧几里得算法过程

模运算

但一般情况下, 我们会使用模运算来减少迭代的次数。

设a(a > b)设为a = kb + c, c < b, 则用减法的欧几里得迭代过程的前面一部分显然是

gcd(kb+c,b)gcd((k1)b+c,b)...gcd(c,b)

上述过程可以简化为

gcd(a,b)gcd(a%b,b)

由此我们可以写出用模运算代替减法运算的代码

模运算代码实现

python

python
def gcd(a, b):
    return a if b == 0 else gcd(b, a % b)

c++

cpp
int gcd(int a, int b) {
 return b ? gcd(b, a % b) : a;
}

一个比较巧妙的点是, 如果a < b, 则gcd(a,b)gcd(b,a%b)gcd(b,a, 通过一次递归调整回了第一个参数较大的情况。

模运算的迭代过程相比减运算是跳跃式的, 所以不一定会经过a==b这个状态, 因此应该以b=0为结束条件。

欧几里得算法的时间复杂度为O(logn), 因为对于a、b(a > b), a %= b至少会让a减少一半以上。

扩展欧几里得算法

裴属定理

裴属定理:对于任意整数a、b, 都能找到两个整数x、y使得ax + by = gcd(a, b).

设a、b的最大公约数为c, 则有a = i * c, b = j * c, 且i、j互质。所以裴属定理的另一种形式是:对于两个互质的整数a、b, 都能找到两个整数x、y使得ax + by = 1

过程

扩展欧几里得算法常用于寻找裴属定理的一组可行解。

ax1+by1=gcd(a,b), x1y1就是我们要求的解。

在欧几里得算法中, 如果要求gcd(a,b), 会递归的求gcd(b,a%b).

bx2+(a%b)y2=gcd(b,a%b).

$\because \ $$gcd(a, b) = gcd(b, a%b)$

 ax1+by1=bx2+(a%b)y2

 a%b=abab

 ax1+by1=bx2+(a%b)y2=bx2+(abab)y2

化简得ax1+by1=ay2+b(x2y2)ab, 所以x1=y2,y1=x2y2ab.

要求x1,y1, 只需先递归的求出x2,y2即可。

在欧几里得算法的递归终点gcd(c,0)中, 要使cx3+0y3=gcd(c,0)=c, 一组可行的解是x3=1,y3=0。到达终点后, 不断回溯对(x,y)进行递推, 最后即可得到关于(a,b)的一组可行解。

代码实现

python

python
def exgcd(a, b):
    if b == 0: 
        return 1, 0
    x, y = exgcd(b, a % b)
    return y, x - y*(a // b)

c++

cpp
void exgcd(int a, int b, int &x, int &y) {
    if (b == 0) {
        x = 1, y = 0;
    } else {
        exgcd(b, a % b, x, y);
        int t = x;
        x = y, y = t - y * (a / b);
    }
}

参考

辗转相除法 - 维基百科,自由的百科全书 (wikipedia.org)

小知识:什么是「欧几里得算法」_吴师兄学算法 (cxyxiaowu.com)

最大公约数 - OI Wiki (oi-wiki.org)

裴蜀定理_百度百科 (baidu.com)

扩展欧几里得算法 - 维基百科,自由的百科全书 (wikipedia.org)