定义#
欧拉函数是由指向 小于等于且与互质的正整数个数 的函数,用表示。
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 的质因数分解为(是质因数),则欧拉函数可以用公式求得:
推导#
首先一个数与互质,说明没有公共的质因子,即不能有、、...等质因子。
那么中有质因子的个数为。
同样,对质因子,中有个 数带质因子。
显然,没有的质因子的数量,就是要减去,即。
加上重复减去的数,根据容斥原理就得到了:
·
朴素算法#
1#
根据定义,直接枚举里与的最大公约数是 1 的个数。
for(int i=1; i<=n; i++)
ans += gcd(n, i)==1;
2#
根据公式
循环求质因子,在过程中计算 ans=ans*(1-1/pi) , 即 ans-= ans/pi
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
- 对每个素数的倍数 j , 有 phi [j] *= (1-1/pi),也即 phi [j] -= phi [j]/pi
- 如果对于某个 i,其 phi [i] != i,说明之前筛过,是合数
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;
}
}
欧拉筛法#
对于某个质数,分两种情况分析:
- 如果是 x 的质因子,则对 来说,pi 已经在欧拉函数里计算了,因此有:
- 如果不是 x 的质因子,则对来说又多了一个质因子,有:
结合欧拉筛法,就可以求 phi [x] 的代码。
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);
}
}
}