扩展的欧几里得算法——递归与非递归实现

来源:互联网 发布:网络诈骗事件 编辑:程序博客网 时间:2024/05/18 01:20

扩展的欧几里得算法(一)——引子

前言

欧几里得算法的目的是求出正整数a,b的最大公因数,记作gcd(a, b)。该算法的原理我就不赘述了,任何一本初等数论的课本都会有详细的介绍。这次的一系列文章的重点放在如何用实际的代码去实现这些算法,我们忽略数学的细节,专注于代码的编写。
欧几里得算法的实现非常简单,它的递归实现为

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

同一种算法,可以有不同的实现,欧几里得算法就是这样一个活生生的例子。用递归实现的好处在于代码简洁明了,清晰易懂,只不过由于调用栈的原因,时间上不如非递归算法高效。我们也可以用非递归算法实现欧几里得算法。

int gcd(int a, int b){    if (b == 0)        return a;    int r = a % b;    while (r) {        a = b;         b = r;        r = a % b;    }    return b;}

那么什么是扩展的欧几里得算法呢?最大公因数有这样一条性质:存在整数x,y使得
ax + by = gcd(a, b)
扩展的欧几里得算法可以求出满足条件的一组x和y,当然这样的整数对(x, y)不是唯一的。

实现

在讲述扩展的欧几里得算法原理之前,我先给出实现,这样没有耐心的小伙伴可以直接从我这里copy代码过去,这也算是功德一件(笑)。

扩展的欧几里得算法常见的实现应该是一面的递归实现,它的原理非常简单。要求出整数对(x,y)满足
ax + by = gcd(a, b),(b != 0)
那么只要知道(x’, y’)满足(a % b)x' + by' =gcd(a, b)
又有a % b = a - (a / b)*b, (这里的'/'号向下取整)
我们可以知道x = x', y = y'-(a/b)x'

递归实现如下:

int e_gcd(int a, int b, int *x, int *y){    if (b == 0) {        *x = 1; *y = 0;        return a;     } else {        int r = e_gcd(b, a%b, y, x);        *y -= (*x)*(a/b);        return r;    }}

当然,扩展欧几里得算法也有非递归实现,它的原理是“列表法”。下面给出实现代码。

int e_gcd(int a, int b, int *x, int *y){    int r, q, s1 = 1, s2 = 0;    if (b == 0) {        *x = 1; *y = 0;        return a;    }    *x = 0, *y = 1;    r = a % b;    q = a / b;    while (r) {        int m = *x, n = *y;        *x = s1 - (*x)*q;        *y = s2 - (*y)*q;        s1 = m; s2 = n;        a  = b; b  = r;        q = a / b;        r = a % b;    }    return b;}

我们可以看出,在代码实现上,扩展欧几里得算法只是在欧几里得算法的基础上增加了对整数对(x,y)的迭代求解而已。总体来说,实现难度不大。

下回预告

  • 到底什么是“列表法”求整数对(x,y)?
  • 列表法的原理是什么?
  • 如何在纸上用列表法求出x, y

请期待我的下一篇博文吧!下次我们接着聊。

0 0
原创粉丝点击