最大公约数
定义
最大公约数即为 Greatest Common Divisor,常缩写为 gcd。
一组整数的公约数,是指同时是这组数中每一个数的约数的数。
一组整数的最大公约数,是指所有公约数里面最大的一个。
对不全为
对不全为
最大公约数与最小公倍数的性质见 数论基础。
那么如何求最大公约数呢?我们先考虑两个数的情况。
欧几里得算法
过程
如果我们已知两个数
不妨设
我们发现如果
我们通过证明可以得到
证明
设
由右边的式子可知
反过来也需要证明:
设
因为左边式子显然为整数,所以
既然两式公约数都是相同的,那么最大公约数也会相同。
所以得到式子
既然得到了
实现
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 7 8 9 10 |
|
1 2 3 4 |
|
递归至 b == 0
(即上一步的 a % b == 0
)的情况再返回值即可。
根据上述递归求法,我们也可以写出一个迭代求法:
1 2 3 4 5 6 7 8 |
|
1 2 3 4 5 6 7 8 |
|
1 2 3 4 |
|
上述算法都可被称作欧几里得算法(Euclidean algorithm)。
另外,对于 C++17,我们可以使用 <numeric>
头中的 std::gcd
与 std::lcm
来求最大公约数和最小公倍数。
注意
在部分编译器中,C++14 中可以用 std::__gcd(a,b)
函数来求最大公约数,但是其仅作为 std::rotate
的私有辅助函数。1使用该函数可能会导致预期之外的问题,故一般情况下不推荐使用。
如果两个数
性质
欧几里得算法的时间效率如何呢?下面我们证明,在输入为两个长为
证明
当我们求
,这时候 ; ,这时候 ,而对 取模会让 至少折半。这意味着这一过程最多发生 次。
第一种情况发生后一定会发生第二种情况,因此第一种情况的发生次数一定 不多于 第二种情况的发生次数。
从而我们最多递归
事实上,假如我们试着用欧几里得算法去求 斐波那契数列 相邻两项的最大公约数,会让该算法达到最坏复杂度。
更相减损术
大整数取模的时间复杂度较高,而加减法时间复杂度较低。针对大整数,我们可以用加减代替乘除求出最大公约数。
过程
已知两数
不妨设
因此,
Stein 算法的优化
如果
考虑一个优化,若
否则,若
优化后的算法(即 Stein 算法)时间复杂度是
证明
若
否则,
算法最多递归
实现
高精度模板见 高精度计算。
高精度运算需实现:减法、大小比较、左移、右移(可用低精乘除代替)、二进制末位 0 的个数(可以通过判断奇偶暴力计算)。
C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
上述代码参考了 libstdc++ 和 MSVC 对 C++17 std::gcd
的实现。在 unsigned int
和 unsigned long long
的数据范围下,如果可以以极快的速度计算 countr_zero
,则 Stein 算法比欧几里得算法来得快,但反之则可能比欧几里得算法慢。
关于 countr_zero
- gcc 有 内建函数
__builtin_ctz
(32 位)或__builtin_ctzll
(64 位)可替换上述代码的countr_zero
; - 从 C++20 开始,头文件
<bit>
包含了std::countr_zero
; - 如果不使用不在标准库的函数,又无法使用 C++20 标准,下面的代码是一种在 Word-RAM with multiplication 模型下经过预处理后
的实现:
1 2 3 4 5 6 7 8 9 |
|
而对于高精度运算,如果实现方法类似 bitset
,则搭配上述对 countr_zero
的实现可以在 O(n / w)
的时间复杂度下完成。但如果不便按二进制位拆分,则只能暴力判断最大的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
更多关于 gcd
实现上快慢的讨论可阅读 Fastest way to compute the greatest common divisor。
多个数的最大公约数
那怎么求多个数的最大公约数呢?显然答案一定是每个数的约数,那么也一定是每相邻两个数的约数。我们采用归纳法,可以证明,每次取出两个数求出答案后再放回去,不会对所需要的答案造成影响。
最小公倍数
接下来我们介绍如何求解最小公倍数(Least Common Multiple, LCM)。
定义
一组整数的公倍数,是指同时是这组数中每一个数的倍数的数。0 是任意一组整数的公倍数。
一组整数的最小公倍数,是指所有正的公倍数里面,最小的一个数。
对整数
对整数
两个数
设
我们发现,对于
最小公倍数等于
由于
所以得到结论是
要求两个数的最小公倍数,先求出最大公约数即可。
多个数
可以发现,当我们求出两个数的
扩展欧几里得算法
扩展欧几里得算法(Extended Euclidean algorithm, EXGCD),常用于求
过程
设
由欧几里得定理可知:
所以
又因为
所以
因为
将
实现
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1 2 3 4 5 |
|
函数返回的值为
值域分析
万幸的是,若
下面给出这一性质的证明。
证明
时, ,必在下一层终止递归。
得到 ,显然 。 时,设 。
因为
所以
因此 成立。
迭代法编写扩展欧几里得算法
首先,当
成立。
已知
将迭代过程中的
据此就可以得到迭代法求 exgcd。
因为迭代的方法避免了递归,所以代码运行速度将比递归代码快一点。
1 2 3 4 5 6 7 8 9 10 11 |
|
如果你仔细观察
最后我们知道
矩阵的解释
对于正整数
其中向下取整符号
易发现欧几里得算法即不停应用该变换,有
令
那么
满足
1 2 3 4 5 6 7 8 9 10 |
|
这种表述相较于递归更简单。
应用
参考资料与链接
本页面最近更新:2024/4/21 20:49:22,更新历史
发现错误?想一起完善? 在 GitHub 上编辑此页!
本页面贡献者:iamtwz, StudyingFather, 383494, Backl1ght, buggg-hfc, Cubik65536, Enter-tainer, gi-b716, Great-designer, hly1204, hsfzLZH1, huaruoji, i-yyi, Ir1d, Koishilll, ksyx, LuoshuiTianyi, Marcythm, MegaOwIer, Menci, mgt, NachtgeistW, ouuan, PwzXxm, shawlleyw, sshwy, tder6, Tiphereth-A, untitledunrevised, VaneHsiung, warzone-oier, WillHouMoe, Xeonacid, Yanjun-Zhao
本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用