Greatest Common Divisor
最大公约数¶
最大公约数即为 Greatest Common Divisor,常缩写为 gcd。
在 素数 一节中,我们已经介绍了约数的概念。
一组数的公约数,是指同时是这组数中每一个数的约数的数。而最大公约数,则是指所有公约数里面最大的一个。
那么如何求最大公约数呢?我们先考虑两个数的情况。
欧几里得算法¶
如果我们已知两个数
不妨设
我们发现如果
我们通过证明可以得到
设
由右边的式子可知
反过来也需要证明:
设
因为左边式子显然为整数,所以
既然两式公约数都是相同的,那么最大公约数也会相同。
所以得到式子
既然得到了
int gcd(int a, int b) {
if (b == 0) return a;
return gcd(b, a % b);
}
递归至 b==0
(即上一步的 a%b==0
) 的情况再返回值即可。
上述算法被称作欧几里得算法(Euclidean algorithm)。
如果两个数
欧几里得算法的时间效率如何呢?下面我们证明,欧几里得算法的时间复杂度为
当我们求
a < b \gcd(a,b)=\gcd(b,a) a \geq b \gcd(a,b)=\gcd(b,a \bmod b) a a O(\log n)
第一种情况发生后一定会发生第二种情况,因此第一种情况的发生次数一定 不多于 第二种情况的发生次数。
从而我们最多递归
事实上,假如我们试着用欧几里得算法去求 斐波那契数列 相邻两项的最大公约数,会让该算法达到最坏复杂度。
多个数的最大公约数¶
那怎么求多个数的最大公约数呢?显然答案一定是每个数的约数,那么也一定是每相邻两个数的约数。我们采用归纳法,可以证明,每次取出两个数求出答案后再放回去,不会对所需要的答案造成影响。
最小公倍数¶
接下来我们介绍如何求解最小公倍数(Least Common Multiple, LCM)。
两个数的¶
首先我们介绍这样一个定理——算术基本定理:
每一个正整数都可以表示成若干整数的乘积,这种分解方式在忽略排列次序的条件下是唯一的。
用数学公式来表示就是
设
我们发现,对于
最小公倍数等于
由于
所以得到结论是
要求两个数的最小公倍数,先求出最大公约数即可。
多个数的¶
可以发现,当我们求出两个数的
扩展欧几里得算法¶
扩展欧几里得算法(Extended Euclidean algorithm, EXGCD),常用于求
证明¶
设
由欧几里得定理可知:
所以
又因为
所以
因为
将 0
递归 x=1,y=0
回去求解。
int Exgcd(int a, int b, int &x, int &y) {
if (!b) {
x = 1;
y = 0;
return a;
}
int d = Exgcd(b, a % b, x, y);
int t = x;
x = y;
y = t - (a / b) * y;
return d;
}
函数返回的值为
迭代法编写拓展欧几里得算法¶
因为迭代的方法避免了递归,所以代码运行速度将比递归代码快一点。
int gcd(int a, int b, int& x, int& y) {
x = 1, y = 0;
int x1 = 0, y1 = 1, a1 = a, b1 = b;
while (b1) {
int q = a1 / b1;
tie(x, x1) = make_tuple(x1, x - q * x1);
tie(y, y1) = make_tuple(y1, y - q * y1);
tie(a1, b1) = make_tuple(b1, a1 - q * b1);
}
return a1;
}
如果你仔细观察
最后我们知道
应用¶
buildLast update and/or translate time of this article,Check the history
editFound smelly bugs? Translation outdated? Wanna contribute with us? Edit this Page on Github
peopleContributor of this article OI-wiki
translateTranslator of this article Visit the original article!
copyrightThe article is available under CC BY-SA 4.0 & SATA ; additional terms may apply.