欧拉函数

欧拉函数

欧拉函数 \(\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)\),所以

\[\begin{aligned} \varphi(n) &= \prod\limits_{i = 1}^{s}\varphi(p_i^{c_i})\\ &= \prod\limits_{i = 1}^{s}p_i^{c_i - 1} \times (p_i - 1)\\ &= \prod\limits_{i = 1}^{s}p_i^{c_i} \times \frac{p_i - 1}{p_i}\\ &= n \times \prod\limits_{i = 1}^{s}\frac{p_i - 1}{p_i} \end{aligned} \]

根据最后一条我们能够筛出 \(\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; }
	}
}
posted @ 2024-01-25 20:09  songszh  阅读(9)  评论(0编辑  收藏  举报