定义
欧拉函数是由n指向小于等于n且与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的质因数分解为p1a×p2b×...×pmk(pi是质因数),则欧拉函数可以用公式求得:
phi(n)=n×(1−p11)×(1−p21)×...×(1−pm1)
推导
首先一个数x与n互质,说明没有公共的质因子,即x不能有p1、p2、…pm等质因子。
那么1...n中有p1质因子的个数为pin。
同样,对质因子pi,1...n中有pin个 数带pi质因子。
显然,没有pi的质因子的数量,就是要减去pin,即n−sum(pin)。
加上重复减去的数,根据容斥原理就得到了:
phi(n)=n−sum(pin)+sum(pi×pjn)−...+(−1)m×p1×p2n×...×pm=n(1−p11)×(1−p21)...(1−pm1)
朴素算法
1
根据定义,直接枚举1...n里与n的最大公约数是1的个数。
1 2
| for(int i=1; i<=n; i++) ans += gcd(n, i)==1;
|
2
根据公式
phi(n)=n×(1−p11)×(1−p21)×...×(1−pm1)
循环求质因子,在过程中计算 ans=ans*(1-1/pi) ,即 ans-= ans/pi
1 2 3 4 5 6 7 8
| 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−pi1)
埃氏筛法的原理是用素数pi 筛所有的倍数j×pi,说明此时j×pi有一个质因数pi,因此可以用上面的公式进行递推。
- 首先 phi[x] = x
- 对每个素数pi的倍数 j , 有phi[j] *= (1-1/pi),也即phi[j] -= phi[j]/pi
- 如果对于某个i,其phi[i] != i,说明之前筛过,是合数
1 2 3 4 5 6 7 8 9
| 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,分两种情况分析:
- 如果是x的质因子,则对x×pi 来说,pi已经在欧拉函数里计算了(1−pi1),因此有:phi(x×pi)=phi(x)×pi
- 如果不是x的质因子,则对x×pi来说又多了一个质因子,有:phi(x×pi)=phi(x)×pi×(1−pi1)=phi(x)×(pi−1)
结合欧拉筛法,就可以求phi[x]的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| 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); } } }
|