欧几里得算法的目的是求出正整数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)的迭代求解而已。总体来说,实现难度不大。
请期待我的下一篇博文吧!下次我们接着聊。