欧拉函数:对正整数 n,欧拉函数是小于 n 的正整数中与 n 互质的数的数目,记作 φ(n)

例如,φ(8)=4,因为 1,3,5,7 均与 8 互质。注意:φ(1)=1

欧拉函数的值

一般方法

n 有标准分解 p1k1p2k2prkr,其中 pi 为互异的质因子,ki1 为质因子的次数。则欧拉函数的值为:

φ(n)=n(11p1)(11p2)(11pr)

需要注意:φ(1)=1

简单证明

若要获得 1n 中的质因子个数,可以将 n 减去 p1 和它的倍数的数量,减去 p2 和它的倍数的数量

写成表达式即为:

nnp1np2npr

但是可以发现,p1,p2 的公倍数、p1,p3 的公倍数 被减去了两次,因此需再加上一次。但又发现 p1,p2,p3 的公倍数、p1,p2,p4 的公倍数 多加了一次,得再减去一次,这便是容斥原理的思维。

最终表达式为:

φ(n)=nnp1np2npr+np1p2+np1p3++npr1prnp1p2p3np1p2p4npr2pr1pr

通过化简即可得到结果式子的形式。

程序实现

该方法仅求得 n 的欧拉函数值

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

编程时需要注意的是,1pi 会被整除得到 0,因此需将 11pi 变形为 pi1pi。同时,对 n 先除再乘,防止溢出。

#include <bits/stdc++.h>

using namespace std;

int main()
{
    int a;
    cin >> a;
    int ans = a;
    for (int i = 2; i <= a / i; i++)
    {
        if (!(a % i))
        {
            ans = ans / i * (i - 1);
            while (!(a % i))
                a /= i;
        }
    }
    if (a > 1)
        ans = ans / a * (a - 1);
    cout << ans << endl;
    return 0;
}

线性筛法

简单证明

通过简单改造线性筛,再筛得质数的同时计算出欧拉函数值。线性筛中有三种情况:

  1. i 为质数
  2. i 能被 prime[j] 整除
  3. i 不能被 prime[j] 整除

我们分类讨论:

i 为质数时,i 与除 1 外互素,因此 φ(i)=i1

i 能被 prime[j] 整除时,prime[j] 即为 i 的一个质因子,那么 iprime[j] 的标准分解的质因子和 i 完全一样,那么:

φ(i)=(11p1)(11p2)(11pr)iφ(iprime[j])=(11p1)(11p2)(11pr)iprime[j]φ(iprime[j])=φ(i)prime[j]

i 不能被 prime[j] 整除时,prime[j] 不时 i 的质因子,但是 iprime[j] 的一个质因子,那么:

φ(i)=(11p1)(11p2)(11pr)iφ(iprime[j])=(11p1)(11p2)(11pr)(11prime[j])iprime[j]φ(iprime[j])=φ(i)prime[j](11prime[j])=φ(i)(prime[j]1)

按照上面的转换方式修改线性筛的代码即可。

程序实现

该方法求得 1n 的所有欧拉函数值

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
const int MAXN = 1e6 + 10;
int prime[MAXN], phi[MAXN], idx;
bool is_prime[MAXN];

void init(int n)
{
    memset(is_prime, 1, sizeof(is_prime));
    is_prime[0] = is_prime[1] = false;
    phi[1] = 1;
    for (int i = 2; i <= n; i++)
    {
        if (is_prime[i])
        {
            prime[idx++] = i;
            phi[i] = i - 1;
        }
        for (int j = 0; j < idx && i * prime[j] <= n; j++)
        {
            is_prime[i * prime[j]] = false;
            if (!(i % prime[j]))
            {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
}