素数筛法|欧拉函数|欧拉筛法 埃氏筛法 详解

定义

欧拉函数是由n nn指向 小于等于n nn且与n nn互质的正整数个数 的函数，用phi(n)phi(n)phi(n)表示。

example

phi(2) = 1，（1）
phi(3) = 2，（1，2）
phi(4) = 2，（1，3）
phi(5) = 4，（1，2，3，4）
phi(6) = 2，（1，5）

公式

结论

假设 n 的质因数分解为p 1 a×p 2 b×...×pmk p_1^a \times p_2^b \times ... \times p_m^kp1a​×p2b​×...×pmk​（pi p_ipi​是质因数），则欧拉函数可以用公式求得：
phi(n)=n×(1−1 p 1)×(1−1 p 2)×...×(1−1 pm)phi(n) = n \times (1- \cfrac{1}{p_1} ) \times (1- \cfrac{1}{p_2} ) \times ... \times (1-\cfrac{1}{p_m})phi(n)=n×(1−p1​1​)×(1−p2​1​)×...×(1−pm​1​)

推导

首先一个数x xx与n nn互质，说明没有公共的质因子，即x xx不能有p 1 p_1p1​、p 2 p_2p2​、...pm p_mpm​等质因子。
那么1...n 1...n1...n中有p 1 p_1p1​质因子的个数为n pi\cfrac{n}{p_i}pi​n​。
同样，对质因子pi p_ipi​，1...n 1...n1...n中有n pi\cfrac{n}{p_i}pi​n​个 数带pi p_ipi​质因子。
显然，没有pi p_ipi​的质因子的数量，就是要减去n pi\cfrac{n}{p_i}pi​n​，即n−sum(n pi)n-sum(\cfrac{n}{p_i})n−sum(pi​n​)。

加上重复减去的数，根据容斥原理就得到了：

·phi(n)=n−sum(n pi)+sum(n pi×pj)−...+(−1)m×n p 1×p 2×...×pm=n(1−1 p 1)×(1−1 p 2)...(1−1 pm)phi(n) = n - sum( \cfrac{n}{p_i} ) + sum( \cfrac{n}{p_i \times p_j} ) - ... + (-1)^m \times \cfrac{n}{p_1 \times p_2} \times ...\times p_m =n(1- \cfrac{1}{p_1}) \times (1-\cfrac{1}{p_2})...(1-\cfrac{1}{p_m})phi(n)=n−sum(pi​n​)+sum(pi​×pj​n​)−...+(−1)m×p1​×p2​n​×...×pm​=n(1−p1​1​)×(1−p2​1​)...(1−pm​1​)

朴素算法

1

根据定义，直接枚举1...n 1...n1...n里与n nn的最大公约数是 1 的个数。

Copy
for(int i=1; i<=n; i++)
ans += gcd(n, i)==1;

2

根据公式

phi(n)=n×(1−1 p 1)×(1−1 p 2)×...×(1−1 pm)phi(n) = n \times (1- \cfrac{1}{p_1} ) \times (1- \cfrac{1}{p_2} ) \times ... \times (1-\cfrac{1}{p_m})phi(n)=n×(1−p1​1​)×(1−p2​1​)×...×(1−pm​1​)

循环求质因子，在过程中计算 ans=ans*(1-1/pi) , 即 ans-= ans/pi

Copy
ans = n;
for(int i=2; i*i<=n; i++) {
if(n%i==0) {
ans -= ans/i;
while(n%i==0) n/=i;
}
}
if(n>1) ans -= ans/n;

筛法解法

埃氏筛法

根据欧拉函数的定义，phi(x)=x×(1−1 pi)phi(x)=x \times (1- \cfrac{1}{p_i})phi(x)=x×(1−pi​1​)

埃氏筛法的原理是用素数pi p_ipi​ 筛所有的倍数j×pi j \times p_ij×pi​，说明此时j×pi j \times p_ij×pi​有一个质因数pi p_ipi​，因此可以用上面的公式进行递推。

首先 phi [x] = x

对每个素数pi p_ipi​的倍数 j , 有 phi [j] *= (1-1/pi)，也即 phi [j] -= phi [j]/pi

如果对于某个 i，其 phi [i] != i，说明之前筛过，是合数

Copy
void shai(int mx) {
for(int i=1; i<=mx; i++)
phi[i] = i;
for(int i=2; i<=mx; i++) {
if(phi[i] != i) continue;
for(int j=i; j<=mx; j+=i)
phi[j] -= phi[j]/i;
}
}

欧拉筛法

对于某个质数pi p_ipi​，分两种情况分析：

如果是 x 的质因子，则对x×pi x \times p_ix×pi​ 来说，pi 已经在欧拉函数里计算了(1−1 pi)(1-\cfrac{1}{p_i})(1−pi​1​)，因此有：phi(x×pi)=phi(x)×pi phi(x \times p_i) = phi(x) \times p_iphi(x×pi​)=phi(x)×pi​

如果不是 x 的质因子，则对x×pi x \times pix×pi来说又多了一个质因子，有：phi(x×pi)=phi(x)×pi×(1−1 pi)=phi(x)×(pi−1)phi(x \times p_i) = phi(x) \times p_i \times (1- \cfrac{1}{p_i}) = phi(x) \times (p_i - 1)phi(x×pi​)=phi(x)×pi​×(1−pi​1​)=phi(x)×(pi​−1)

结合欧拉筛法，就可以求 phi [x] 的代码。

Copy
void shai(int mx) {
phi[1] = 1;
for(int i=2; i<=mx; i++) {
if(!notPrime[i]) {
p[++last] = i;
phi[i] = i-1;
}
for(int j=1; j<=last; j++) {
int t = i * p[j];
if(t>=mx) break;
notPrime[t] = true;
if(i % p[j]==0) {
phi[t] = phi[i] * p[j];
break;
} else
phi[t]= phi[i] * (p[j]-1);
}
}
}