欧拉函数(Euler’s Totient Function),记作 ϕ ( n ) \phi(n) ϕ(n),是数论中的一个重要函数,用于计算小于或等于正整数 n n n 且与 n n n 互质的正整数的个数。换句话说,欧拉函数 ϕ ( n ) \phi(n) ϕ(n) 给出了在 1 1 1 到 n n n 之间与 n n n 互质的整数的数量。
欧拉函数的性质
-
对于质数 p p p:
- 如果 p p p 是一个质数,那么 ϕ ( p ) = p − 1 \phi(p) = p - 1 ϕ(p)=p−1,因为 1 1 1 到 p − 1 p-1 p−1 的所有整数都与 p p p 互质。
-
对于质数的幂 p k p^k pk:
- 如果 p p p 是一个质数,且 k k k 是一个正整数,那么 ϕ ( p k ) = p k − p k − 1 \phi(p^k) = p^k - p^{k-1} ϕ(pk)=pk−pk−1。这是因为在 1 1 1 到 p k p^k pk 之间,只有 p p p 的倍数不与 p k p^k pk 互质,而 p p p 的倍数有 p k − 1 p^{k-1} pk−1 个。
-
对于两个互质的整数 m m m 和 n n n:
- 如果 m m m 和 n n n 互质,即 gcd ( m , n ) = 1 \gcd(m, n) = 1 gcd(m,n)=1,那么 ϕ ( m × n ) = ϕ ( m ) × ϕ ( n ) \phi(m \times n) = \phi(m) \times \phi(n) ϕ(m×n)=ϕ(m)×ϕ(n)。这个性质称为欧拉函数的积性。
-
对于一般的正整数 n n n:
- 如果
n
n
n 的质因数分解为
n
=
p
1
k
1
×
p
2
k
2
×
⋯
×
p
m
k
m
n = p_1^{k_1} \times p_2^{k_2} \times \cdots \times p_m^{k_m}
n=p1k1×p2k2×⋯×pmkm,那么欧拉函数可以表示为:
ϕ ( n ) = n × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) × ⋯ × ( 1 − 1 p m ) \phi(n) = n \times \left(1 - \frac{1}{p_1}\right) \times \left(1 - \frac{1}{p_2}\right) \times \cdots \times \left(1 - \frac{1}{p_m}\right) ϕ(n)=n×(1−p11)×(1−p21)×⋯×(1−pm1)
- 如果
n
n
n 的质因数分解为
n
=
p
1
k
1
×
p
2
k
2
×
⋯
×
p
m
k
m
n = p_1^{k_1} \times p_2^{k_2} \times \cdots \times p_m^{k_m}
n=p1k1×p2k2×⋯×pmkm,那么欧拉函数可以表示为:
第四个性质可以这样来理解,我们取一种简单的情况:
n
=
p
1
k
1
×
p
2
k
2
n = p_1^{k_1} \times p_2^{k_2}
n=p1k1×p2k2。质因数分解之后,首先去除
p
1
p_1
p1的倍数,一共有
⌊
n
p
1
⌋
\left\lfloor \frac{n}{p_1}\right\rfloor
⌊p1n⌋个,同理去除
p
2
p_2
p2的倍数,一共有
⌊
n
p
2
⌋
\left\lfloor \frac{n}{p_2}\right\rfloor
⌊p2n⌋个。再加上去除了两次的
p
1
,
p
2
p_1,p_2
p1,p2的公倍数,有
⌊
n
p
1
×
p
2
⌋
\left\lfloor \frac{n}{p_1\times p_2}\right\rfloor
⌊p1×p2n⌋个。因此
ϕ
(
n
)
=
n
−
⌊
n
p
1
⌋
−
⌊
n
p
2
⌋
+
⌊
n
p
1
×
p
2
⌋
\phi (n) = n - \left\lfloor \frac{n}{p_1}\right\rfloor - \left\lfloor \frac{n}{p_2}\right\rfloor + \left\lfloor \frac{n}{p_1\times p_2}\right\rfloor
ϕ(n)=n−⌊p1n⌋−⌊p2n⌋+⌊p1×p2n⌋,而
n
n
n是可以被
p
1
,
p
2
p_1,p_2
p1,p2整除的(因为是
n
n
n的质因数),所以化简得到
ϕ
(
n
)
=
n
×
(
1
−
1
p
1
−
1
p
2
+
1
p
1
×
p
2
)
=
n
×
(
1
−
1
p
1
)
×
(
1
−
1
p
2
)
\phi(n) = n \times (1 - \frac{1}{p_1} - \frac{1}{p_2} + \frac{1}{p_1\times p_2})=n\times(1-\frac{1}{p_1})\times(1-\frac{1}{p_2})
ϕ(n)=n×(1−p11−p21+p1×p21)=n×(1−p11)×(1−p21)
面对更加多质因数的情况,也是依据容斥原理进行推导,这里就不详细介绍了。
欧拉函数的计算示例
-
计算 ϕ ( 9 ) \phi(9) ϕ(9):
- 9 = 3 2 9 = 3^2 9=32,所以 ϕ ( 9 ) = 9 − 3 = 6 \phi(9) = 9 - 3 = 6 ϕ(9)=9−3=6。
- 与 9 互质的数有 1, 2, 4, 5, 7, 8,共 6 个。
-
计算 ϕ ( 12 ) \phi(12) ϕ(12):
-
12
=
2
2
×
3
12 = 2^2 \times 3
12=22×3,所以:
ϕ ( 12 ) = 12 × ( 1 − 1 2 ) × ( 1 − 1 3 ) = 12 × 1 2 × 2 3 = 4 \phi(12) = 12 \times \left(1 - \frac{1}{2}\right) \times \left(1 - \frac{1}{3}\right) = 12 \times \frac{1}{2} \times \frac{2}{3} = 4 ϕ(12)=12×(1−21)×(1−31)=12×21×32=4 - 与 12 互质的数有 1, 5, 7, 11,共 4 个。
-
12
=
2
2
×
3
12 = 2^2 \times 3
12=22×3,所以:
-
计算 ϕ ( 15 ) \phi(15) ϕ(15):
-
15
=
3
×
5
15 = 3 \times 5
15=3×5,且 3 和 5 互质,所以:
ϕ ( 15 ) = ϕ ( 3 ) × ϕ ( 5 ) = ( 3 − 1 ) × ( 5 − 1 ) = 2 × 4 = 8 \phi(15) = \phi(3) \times \phi(5) = (3 - 1) \times (5 - 1) = 2 \times 4 = 8 ϕ(15)=ϕ(3)×ϕ(5)=(3−1)×(5−1)=2×4=8 - 与 15 互质的数有 1, 2, 4, 7, 8, 11, 13, 14,共 8 个。
-
15
=
3
×
5
15 = 3 \times 5
15=3×5,且 3 和 5 互质,所以:
练习
欧拉函数
ACWing 欧拉函数
给定
n
n
n个正整数
a
i
a_i
ai,出每个数的欧拉函数。需要注意的是,在利用公式计算欧拉函数,需要先除后乘
,这是为了防止溢出
#include <iostream>
#include <vector>
using namespace std;
/**
* 分解整数x的质因数
* @param number 需要分解质因数的整数
* @return 返回一个包含所有质因数的向量
*/
vector<int> decomposeIntoPrimeFactors(int number) {
vector<int> primeFactors;
// 从最小的质数2开始,尝试找到所有的质因数
for (int divisor = 2; divisor <= number / divisor; ++divisor) {
if (number % divisor == 0) {
primeFactors.push_back(divisor); // 将质因数加入结果向量
}
// 除以当前质因数直到不能整除为止
while (number % divisor == 0) {
number /= divisor;
}
}
// 如果剩余的number大于1,则它本身也是一个质因数
if (number > 1) {
primeFactors.push_back(number);
}
return primeFactors;
}
/**
* 计算欧拉函数phi(x),即小于x且与x互质的正整数的数量
* @param number 输入整数
* @return 返回phi(x)的值
*/
int calculateEulerTotient(int number) {
auto primeFactors = decomposeIntoPrimeFactors(number);
int totientValue = number;
// 根据公式phi(x) = x * (1 - 1/p1) * (1 - 1/p2) ... (1 - 1/pn)
for (int factor : primeFactors) {
totientValue = totientValue / factor * (factor - 1);
}
return totientValue;
}
int main() {
ios::sync_with_stdio(false); // 提高I/O效率
cin.tie(0);
cout.tie(0);
int testCases;
cin >> testCases; // 输入测试案例的数量
while (testCases--) {
int number;
cin >> number; // 输入每个测试案例的数字
// 计算并输出该数字的欧拉函数值
int eulerTotientValue = calculateEulerTotient(number);
cout << eulerTotientValue << endl;
}
return 0;
}
筛法求欧拉函数
ACWing 筛法求欧拉函数
给定一个正整数
n
n
n,求
1
∼
n
1∼n
1∼n 中每个数的欧拉函数之和。为了正确计算从
1
1
1到
n
n
n的所有整数的欧拉函数之和,可以使用线性筛法来同时计算每个数的欧拉函数值并累加它们。以下是详细的步骤:
- 线性筛法:利用埃拉托斯特尼筛法或线性筛法来标记质数,并在此过程中计算每个数的欧拉函数值。
- 计算欧拉函数:根据欧拉函数的定义,如果一个数 n 的质因数分解为
p
1
k
1
×
p
2
k
2
×
…
×
p
m
k
m
p_1^{k_1} \times p_2^{k_2} \times \ldots \times p_m^{k_m}
p1k1×p2k2×…×pmkm,则 φ(n) 可以表示为:
ϕ ( n ) = n × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) × … × ( 1 − 1 p m ) \phi(n) = n \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times \ldots \times (1 - \frac{1}{p_m}) ϕ(n)=n×(1−p11)×(1−p21)×…×(1−pm1)
接着,我们来看代码种应当如何实现。首先,让我们会议一下线性筛法:
void linearSieve(int n){
memset(isPrime, true, sizeof isPrime);
isPrime[0] = isPrime[1] = false;
for(int i=2;i<=n;i++){
if(isPrime[i]){
prime[primeCount++] = i;
}
for(int j = 0;j<primeCount && i * prime[j] <= n;j++){
isPrime[i*prime[j]] = false;
if(i % prime[j] == 0)
break;
}
}
}
接着,我们很容易可以想出,在判断数字是质数的时候,可以将其欧拉函数值设置为i - 1
。另外的核心就是在向后筛质数的时候,变量i
充当的是一个倍数的作用,这也是线性筛法确保每个合数只被遍历一遍的原因。我们又知道,欧拉函数值是具有可积性的。那么,当我筛到
p
r
i
m
e
[
j
]
prime[j]
prime[j]的倍数的时候,倍数的欧拉函数则可以用
ϕ
(
i
)
∗
ϕ
(
p
r
i
m
e
[
j
]
)
\phi (i) * \phi (prime[j])
ϕ(i)∗ϕ(prime[j])来计算其欧拉函数值。但是,可积性的前提是互质
,也就是说,当i % prime[j] != 0
,我们才可以用
ϕ
(
i
)
∗
ϕ
(
p
r
i
m
e
[
j
]
)
\phi (i) * \phi (prime[j])
ϕ(i)∗ϕ(prime[j])来更新
ϕ
(
i
∗
p
r
i
m
e
[
j
]
)
\phi (i * prime[j])
ϕ(i∗prime[j])。而对于i % prime[j] == 0
时,说明
p
r
i
m
e
[
j
]
prime[j]
prime[j]也是
i
i
i的一个质因子,而欧拉函数公式是
ϕ
(
n
)
=
n
×
(
1
−
1
p
1
)
×
(
1
−
1
p
2
)
×
…
×
(
1
−
1
p
m
)
\phi(n) = n \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times \ldots \times (1 - \frac{1}{p_m})
ϕ(n)=n×(1−p11)×(1−p21)×…×(1−pm1)
而
1
−
1
p
r
i
m
e
[
j
]
1 - \frac{1}{prime[j]}
1−prime[j]1这个因子显然在
ϕ
(
i
)
\phi(i)
ϕ(i)中计算过了,我们只需要将
ϕ
(
i
)
\phi (i)
ϕ(i)的基数
i
i
i扩大
p
r
i
m
e
[
j
]
prime[j]
prime[j]倍即可。以下是一个使用线性筛法高效计算从
1
1
1到
n
n
n的所有整数的欧拉函数之和的实现:
#include <iostream>
#include <cstring>
using namespace std;
const int maxn = 1e6 + 7;
int phi[maxn]; // 存储每个数的欧拉函数值
bool isPrime[maxn]; // 标记是否为质数
int prime[maxn]; // 存储质数
int primeCount = 0; // 质数的数量
void linearSieveAndEulerPhiSum(int n) {
memset(isPrime, true, sizeof(isPrime)); // 初始化所有数为质数
long long sum = 1; // 用于存储欧拉函数之和,值为1的欧拉函数值
for (int i = 2; i <= n; ++i) {
if (isPrime[i]) {
prime[primeCount++] = i;
phi[i] = i - 1; // 如果i是质数,则φ(i) = i - 1
}
for (int j = 0; j < primeCount && i * prime[j] <= n; ++j) {
isPrime[i * prime[j]] = false;
if (i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
} else {
phi[i * prime[j]] = phi[i] * (prime[j] - 1);
}
}
sum += phi[i];
}
cout << sum << endl;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
linearSieveAndEulerPhiSum(n);
return 0;
}