欧拉函数
搞懂欧拉函数:从定义到代码实现
欧拉函数(Euler’s Totient Function,
在数论和算法竞赛中,我们经常会遇到一个神秘的符号:
1 什么是欧拉函数?
欧拉函数
对于正整数
, 表示在小于等于 的正整数中,与 互质的数的个数。
注:互质(Coprime)的意思是两个数的最大公约数
举个栗子:
我们来看
(互质)✅ (不互质) (不互质) (不互质) (互质)✅ (不互质)
与 6 互质的数只有 1 和 5,共 2 个。所以
2 核心计算公式(推导原理)
如果
根据算术基本定理,任意正整数
欧拉函数的通项公式为:
为什么是这个公式?
利用容斥原理可以通俗理解:
- 我们想求
到 中与 互质的数。 - 初始假设所有数都互质,共
个。 - 减去
的倍数,剩下 。 - 再减去
的倍数……以此类推。 - 注意:欧拉函数只和
的质因子有关,和质因子的指数(次数)无关。
转换为代码逻辑
在计算机中,浮点数运算(
所以在代码中,对于每一个质因子 res = res / p * (p - 1)。
3 代码实现详解
这就是你手中那段代码的完整逻辑解析。这个算法的时间复杂度是
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
typedef long long ll;
// 试除法求欧拉函数
// 输入 n,返回 phi(n)
ll get_phi(ll n) {
ll res = n; // 初始化结果为 n,对应公式中的 n
// 1. 遍历寻找质因子
// 我们只需要遍历到 sqrt(n) 即可,这和判断素数的逻辑一样
for (ll i = 2; i * i <= n; ++i) {
// 2. 如果 i 能整除 n,说明 i 是 n 的一个质因子
if (n % i == 0) {
// 3. 应用公式:res = res * (1 - 1/i)
// 变形为整数运算:res = res / i * (i - 1)
res = res / i * (i - 1);
// 4. 【关键】除尽当前的质因子 i
// 欧拉函数只跟“有哪些质因子”有关,跟因子的个数无关
// 例如 12 = 2^2 * 3,我们处理 2 的时候,要把 12 里的两个 2 都除掉,变成 3
while (n % i == 0) {
n /= i;
}
}
}
// 5. 处理剩余的那个大质因子
// 如果循环结束后 n > 1,说明 n 本身还剩下一个大于 sqrt(原n) 的质因子
// 例如 n = 14 (2 * 7),循环处理了 2,剩下 7,7 > sqrt(14),需要再处理一次
if (n > 1) {
res = res / n * (n - 1);
}
return res;
}
4 关键点 Q&A
Q1: 为什么循环只到
Q2: while (n % i == 0) n /= i; 这行代码有什么用?
A: 这是为了保证
- 当
时,我们除掉了 中所有的因子 2。 - 当枚举到
时,由于 已经不包含因子 2 了,所以 绝不可能被 4 整除。 - 因此,
if (n % i == 0)成立时,一定是质数。这也避免了我们重复计算同一个质因子。
Q3: 复杂度是多少?
A: 时间复杂度是
5 总结
求单个数字欧拉函数的步骤:
- 初始化答案
res = n。 - 从
到 枚举质因子 。 - 如果
是因子: - 更新答案:
res = res / p * (p - 1)。 - 将
中所有的 除干净。
- 更新答案:
- 如果最后
,说明还有一个大质因子,再更新一次答案。
练习题目
下一步建议
这篇博客涵盖了求单个数字欧拉函数的方法。如果你在做算法题时,需要求
这种情况下,你需要学习线性筛(欧拉筛)求欧拉函数,它可以在