算法介绍
在数论,对正整数n,欧拉函数是小于等于n的正整数中与n互质的数的数目
ϕ ( n ) \phi(n) ϕ(n)的值为是小于等于n的正整数中与n互质的数的数目
公式
求解欧拉函数公式为
假设 n n n有 t t t个质因子,有如下公式
-
公式:
ϕ ( n ) = n ∗ ( 1 − 1 p 1 ) ∗ ( 1 − 1 p 2 ) ∗ . . . ∗ ( 1 − 1 p t ) \phi(n)=n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})*...*(1-\frac{1}{p_t}) ϕ(n)=n∗(1−p11)∗(1−p21)∗...∗(1−pt1)
-
证明:
根据容斥定理,假设 n n n的质因子有 p 1 , p 2 p1,p2 p1,p2,那么有 n p 1 \frac{n}{p1} p1n个数与 n n n不互质, n p 2 \frac{n}{p_2} p2n个数与 n n n不互质,但是 n p 1 ∗ p 2 \frac{n}{p_1*p_2} p1∗p2n被多减去了一次因此要加回来…所以合并一下就是 ϕ ( n ) = n ∗ ( 1 − 1 p 1 ) ∗ ( 1 − 1 p 2 ) \phi(n)=n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2}) ϕ(n)=n∗(1−p11)∗(1−p21)如果有多个质因子也一样根据容斥的思想计算,一样可以得到结果。
-
举例:
ϕ ( 6 ) = 2 \phi(6)=2 ϕ(6)=2 因为数字 1 , 5 1,5 1,5和 6 6 6互质
需要注意 ϕ ( 1 ) = 1 \phi(1)=1 ϕ(1)=1
性质
- 当 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1时, ϕ ( a ) ∗ ϕ ( b ) = ϕ ( a b ) \phi(a)*\phi(b)=\phi(ab) ϕ(a)∗ϕ(b)=ϕ(ab)
- a ∣ b a|b a∣b时, ϕ ( a b ) = ϕ ( a ) ∗ b \phi(ab)=\phi(a)*b ϕ(ab)=ϕ(a)∗b
- p p p是质数, ϕ ( p n ) = p n − 1 ∗ ( p − 1 ) \phi(p^n)=p^{n-1}*(p-1) ϕ(pn)=pn−1∗(p−1)
性质证明
-
当 g c d ( a , b ) = 1 gcd(a,b)=1 gcd(a,b)=1时, ϕ ( a ) ∗ ϕ ( b ) = ϕ ( a b ) \phi(a)*\phi(b)=\phi(ab) ϕ(a)∗ϕ(b)=ϕ(ab)
根据公式, a , b a,b a,b如果互质,那么说明没有公共质因子, ϕ ( a b ) \phi(ab) ϕ(ab)的展开就是 ϕ ( a ) ∗ ϕ ( b ) \phi(a)*\phi(b) ϕ(a)∗ϕ(b)
-
a ∣ b a|b a∣b时, ϕ ( a b ) = a ∗ ϕ ( b ) \phi(ab)=a*\phi(b) ϕ(ab)=a∗ϕ(b)
根据公式,如果 a ∣ b a|b a∣b( b % a = 0 b\%a=0 b%a=0),那么说明 a a a拥有的质因子 b b b一定有, ϕ ( a b ) \phi(ab) ϕ(ab)展开, a a a对展开式的质因子那几项没贡献作用,所以相当于直接乘 a a a。
-
p p p是质数, ϕ ( p n ) = p n − 1 ∗ ( p − 1 ) \phi(p^n)=p^{n-1}*(p-1) ϕ(pn)=pn−1∗(p−1),质数的质因子是自己本身,所以直接带公式即可…
常用板子
-
求解 ϕ ( n ) \phi(n) ϕ(n)
思路:试除法求质因子,求的过程中计算即可,时间复杂度 O ( n ) O(\sqrt{n}) O(n)
int n; cin>>n; ll res=n; for(int i=2;i<=n/i;i++){ if(n%i==0)res=res/i*(i-1); while(n%i==0)n/=i; } if(n>1)res=res/n*(n-1); cout<<res<<endl;
-
求解 ∑ i = 1 n ϕ ( i ) \sum_{i=1}^n\phi(i) ∑i=1nϕ(i)
思路:可以用埃氏筛法或线性筛法,要运用到上面推导的三个性质。
- 埃氏筛法
/*
埃氏筛法的过程中,我们对质数进行翻倍,那么每个数字会被自己所有的质因子筛一遍,我们根据这个性质来“顺便”求解欧拉函数
*/
#include <iostream>
const int N=1e6+5;
int phi[N];
int main()
{
int n;
cin>>n;
long long res=1;
for(int i=1;i<=n;i++)phi[i]=i;
for(int i=2;i<=n;i++){
if(phi[i]==i){
for(int j=i;j<=n;j+=i){
phi[j]=phi[j]/i*(i-1);
}
}
res+=phi[i];
}
cout<<res;
}
- 线性筛法
/*
利用线性筛法的特点:每个数字有且仅被最小质因子筛一遍
*/
#include <iostream>
const int N=1e6+5;
typedef long long ll;
using namespace std;
int phi[N]={0,1};
int p[N],cnt;
bool book[N];
int main()
{
int n;
cin>>n;
ll res=1;
for(int i=2;i<=n;i++){
if(phi[i]==0)p[++cnt]=i,phi[i]=i-1;//没被筛到过,表明是质数 性质三
for(int j=1;p[j]<=n/i;j++){
if(i%p[j]==0){
phi[i*p[j]]=phi[i]*p[j];//性质2
break;
}
phi[i*p[j]]=phi[i]*phi[p[j]];//性质1 phi[p[j]]在之前以及被计算过
}
res+=phi[i];
}
cout<<res;
}