最大公约数
辗转相除法不断把“大问题”换成“除数和余数”的小问题,直到余数为 $0$,最后的非零数就是最大公约数。
一句话算法
辗转相除法不断把“大问题”换成“除数和余数”的小问题,直到余数为
问题模型
给定两个整数
最大公约数是同时整除
因为
朴素做法可以从
核心直觉
设:
这里
如果一个数
也就是说,
所以:
这就是欧几里得算法的核心。
“记忆方式”
每一步都把 (a, b) 变成 (b, a % b)。左边丢掉,右边变余数。
算法步骤
- 若
,答案就是 。 - 否则计算余数
。 - 把问题从
变成 。 - 重复上述过程,直到第二个数变成
。
以
算法证明
核心不变量:每一步替换后,两个数的所有公因子集合不变。
设:
其中
-
从左到右
若
且 ,则: 又因为
,所以: 因此
也是 的公因子。 -
从右到左
若
且 ,则: 又因为
,所以: 因此
也是 的公因子。 -
结论
的公因子集合和 的公因子集合完全相同,所以最大公约数也相同:
每次取余后第二个数严格变小,过程一定会结束。结束时
复杂度分析
- 时间复杂度:
。 - 空间复杂度:迭代写法为
。
若使用递归写法,递归栈空间为
代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <bits/stdc++.h>
using namespace std;
int gcd(int a, int b) {
if (a < 0) a = -a;
if (b < 0) b = -b;
while (b != 0) {
int r = a % b;
a = b;
b = r;
}
return a;
}
int main() {
int a, b;
cin >> a >> b;
cout << gcd(a, b) << "\n";
return 0;
}
测试用例
输入:
44 12
输出:
4
过程:
44 % 12 = 8
12 % 8 = 4
8 % 4 = 0
所以答案是 4。
再看一个互质例子:
35 64
输出:
1
应用分类详解
最大公约数的本质是:找两个整数共同拥有的最大“整除单位”。凡是题目里出现“约分、整除、周期同步、比例化简、线性组合”等信号,都应该想到 gcd。
一、分数约分与比例化简
典型模式: 需要把两个整数的比例化成最简形式。
识别信号: 题面出现“最简分数”“斜率比较”“比例相同”“方向向量去重”。
核心建模: 用
| 应用场景 | 经典题目 | 核心思路 |
|---|---|---|
| 分数约分 | luogu-P1888 | 分子分母同时除以 gcd |
| 方向向量去重 | LeetCode 149 | 斜率用约分后的 (dx, dy) 表示 |
二、整除结构与数论基础
典型模式: 问两个数是否有公共因子,或需要用互质性质推出结论。
识别信号: 题面出现“互质”“约数”“倍数”“整除”“最大公因数”。
核心建模: 若
| 应用场景 | 经典题目 | 核心思路 |
|---|---|---|
| 判断互质 | luogu-P2152 | 先求 gcd,再根据是否为 |
| 欧拉函数预备 | luogu-P2158 | 统计互质关系时 gcd 是基础工具 |
三、最小公倍数与周期同步
典型模式: 两个周期什么时候再次同时发生。
识别信号: 题面出现“周期”“同时出现”“下一次相遇”“最小公倍数”。
核心建模: 先求 gcd,再用:
| 应用场景 | 经典题目 | 核心思路 |
|---|---|---|
| 周期同步 | luogu-P1029 | gcd 与 lcm 共同约束两个数 |
| 多周期合并 | LeetCode 1201 | 用 gcd/lcm 处理容斥计数 |
四、扩展欧几里得的入口
典型模式: 要求解整数方程
识别信号: 题面出现“不定方程”“线性组合”“模逆元”“同余方程”。
核心建模: 方程
| 应用场景 | 经典题目 | 核心思路 |
|---|---|---|
| 线性同余方程 | luogu-P1082 | 用 exgcd 求模逆元 |
| 不定方程 | luogu-P5656 | 先判断 gcd 是否整除常数项 |
经典例题
1. luogu-P1888
给出三个数,按要求输出最简比例。核心操作是排序后取分子分母,再用 gcd 约分。
2. luogu-P1029
已知两个数的 gcd 和 lcm,反推满足条件的数对。利用
3. luogu-P1082
求乘法逆元,是扩展欧几里得的模板题。普通 gcd 给出递归骨架,exgcd 在这个骨架上回代出系数。
参考
- 旧版文章:
Rbook_ejs_old/book/math/numberTheory/gcd/index.md