欧拉函数
欧拉函数
欧拉函数 \(\varphi(n)\) 表示 \(1 \sim n - 1\) 中与 \(n\) 互质的数的个数。显然的,当 \(n\) 为质数,有 \(\varphi(n) = n - 1\)。
性质与推导
-
显然的,当 \(\gcd(a,b)\),有 \(\varphi(a \times b) = \varphi(a) \times \varphi(b)\),oi-wiki 上管这个叫做积性函数。
-
显然的,当 \(b \bmod a = 0\),有 \(\varphi(a \times b) = a \times \varphi(b)\)。
-
设 \(n = \prod\limits_{i = 1}^{s} p_i^{c_i}\),即将 \(n\) 进行唯一分解,有 \(\varphi(n) = n \times \prod\limits_{i = 1}^{s}\frac{p_i - 1}{p_i}\),证明如下:
\(\varphi(p^c) = p^c - p^{c - 1} = p^{c - 1} \times (p - 1)\),显然,值不超过 \(p^c\) 的自然数中,有 \(p^{c - 1}\) 个 \(p\) 的倍数,故可得上式;
由欧拉函数的积性,当 \(\gcd(a,b)\),有 \(\varphi(a \times b) = \varphi(a) \times \varphi(b)\),所以
根据最后一条我们能够筛出 \(\varphi(n)\)。
$\tt{Link}$
il int get_phi(int x) {
int phi = x;
for (int i = 2; i * i <= x; ++ i ) {
if (x % i == 0) {
phi = phi / i * (i - 1);
while (x % i == 0) x /= i;
}
}
if (x > 1) phi = phi / x * (x - 1);
return phi;
}
筛法
但是,当我们需要大量地求欧拉函数的值的时候,显然不能使用以上方法。
这里介绍两种筛法。
埃氏筛
我们可以根据 \(\varphi(n) = n \times \prod\limits_{i = 1}^{s} \frac{p_i - 1}{p_i}\) 这个式子,进行递推,每当枚举到 \(phi_i = i - 1\),即 \(i\) 为质数,我们可以将存在 \(i\) 这个质因子的数 \(n\) 都乘上 \(\frac{i - 1}{i}\),可以证明这是正确的。
时间复杂度应为 \(\mathcal{O(n \log \log n)}\)。
$\tt{Link}$
for (int i = 1; i <= n; ++ i ) phi[i] = i;
for (int i = 2; i <= n; ++ i )
if (phi[i] == i) {
for (int j = 1; j * i <= n; ++ j )
phi[i * j] *= (i - 1) / i;
}
欧拉筛
在欧拉筛筛质数中,我们是考虑每个合数都被其最小质因子给筛掉的,由 \(\varphi(n)\) 的一些性质,我们可以在筛法中加入以下内容:
设 \(m\) 为被 \(n \cdot p\) 筛掉的数,那么当 \(n \bmod p = 0\),显然有 \(\varphi(m) = m \times \prod\limits_{i = 1}^{s} \frac{p_i - 1}{p_i} = p \times \varphi(n)\);否则,\(n \mid p\),\(\varphi(m) = (p - 1) \times \varphi(n)\)。
时间复杂度为 \(O(n)\)。
$\tt{Link}$
phi[1] = vis[1] = 1;
for (int i = 2; i < N; ++ i ) {
if (!vis[i]) { p[++ tot] = i; phi[i] = i - 1; }
for (int j = 1; i * p[j] < N && j <= tot; ++ j ) {
vis[i * p[j]] = 1;
if (i % p[j]) phi[i * p[j]] = phi[i] * (p[j] - 1);
else { phi[i * p[j]] = phi[i] * p[j]; break; }
}
}