筛法求欧拉函数

欧拉函数

欧拉函数的定义

1∼n1 \sim n1n中与n互质的数的个数为欧拉函数,记为φ(n)\varphi(n)φ(n)
比如φ(1)\varphi(1)φ(1)=1,φ(2)\varphi(2)φ(2)=1,φ(10)\varphi(10)φ(10)=4

欧拉函数的性质

  1. 如果p是质数,那么φ(p)\varphi(p)φ(p)=p−1p-1p1
  2. 如果p是质数,那么φ(pk)\varphi({p^k})φ(pk)=(p−1)pk−1(p-1) p^{k-1}(p1)pk1
  3. 如果gcd(n,m)=1gcd(n,m)=1gcd(n,m)=1,那么φ(nm)\varphi(nm)φ(nm)=φ(n)∗φ(m)\varphi(n) * \varphi(m)φ(n)φ(m)

这里简单介绍1,2的,对于第三点证明过于繁琐,感兴趣的同学下去寻找证明方法。

  1. 如果p是质数,很明显在1∼p1 \sim p1p中,除p外其他都与p互质,所以φ(p)\varphi(p)φ(p)=p−1p-1p1
  2. 如果p是质数,pk{p^k}pk的质因子有p,所以在1∼pk1 \sim {p^k}1pk中,与pk{p^k}pk互质的数也就是与p互质,所以求出在这个 范围与p互质的数有多少就行。
    我们已经知道在1∼p1 \sim p1p中,与p互质的数有p−1p-1p1个,那么在p+1∼2pp+1 \sim 2pp+12p之间,与p互质的数有哪些呢,根据辗转相除法也知道在这个范围除了2p2p2p,其他都与p互质,所以对于pk{p^k}pk,可以看成
    p⋯2p⋯3p⋯4p⋯pkp \cdots 2p \cdots 3p \cdots 4p \cdots p^kp2p3p4ppk
    每一段与pk{p^k}pk互质的数有p−1p-1p1个,那么总共有多少段呢,已经知道每一段长p,总长为pkp^kpk,总共会有pk/p=pk−1段p^k /p =p^{k-1}段pk/p=pk1
    相乘起来得到φ(pk)\varphi({p^k})φ(pk)=(p−1)pk−1(p-1) p^{k-1}(p1)pk1

怎么求欧拉函数

根据算术基本定理(唯一分解定理)我们知道一个数n可以表示为:
n=∏i=1kpiain=\prod_{i=1}^k {p_i^{a_i}}n=i=1kpiai
pi和pj(i≠j)p_i 和 p_j (i \neq j)pipj(i=j)是互质的,也就是说φ(pi∗pj)\varphi(p_i * p_j)φ(pipj)=φ(pi)∗φ(pj)\varphi(p_i) * \varphi(p_j)φ(pi)φ(pj)
因为piai和pjajp_i^{a_i} 和 p_j^{a_j}piaipjaj也是互质的,所以φ(piai∗pjaj)\varphi(p_i^{a_i} * p_j^{a_j})φ(piaipjaj)=φ(piai)∗φ(pjaj)\varphi(p_i^{a_i}) * \varphi(p_j^{a_j})φ(piai)φ(pjaj)
那么我们就可以根据算术基本定理来计算φ(n)\varphi(n)φ(n)
φ(n)=φ(∏i=1kpiai)\varphi(n) = \varphi(\prod_{i=1}^k {p_i^{a_i}})φ(n)=φ(i=1kpiai)
=∏i=1kφ(piai)\hspace{3em}= {\prod_{i=1}^k} {\varphi(p_i^{a_i})}=i=1kφ(piai)
=∏i=1k(pi−1)piai−1\hspace{3em}= {\prod_{i=1}^k} {(p_i - 1)}{p_i}^{a_i - 1}=i=1k(pi1)piai1
=∏i=1kpiai(1−1pi)\hspace{3em}= {\prod_{i=1}^k} {p_i^{a_i}}{(1- \frac{1}{ p_i})}=i=1kpiai(1pi1)
=∏i=1kpiai∗∏i=1k(1−1pi)\hspace{3em}= {\prod_{i=1}^k} p_i^{a_i} * {\prod_{i=1}^k} {(1- \frac{1}{ p_i})}=i=1kpiaii=1k(1pi1)
=n∗∏i=1k(1−1pi)\hspace{3em}= n * {\prod_{i=1}^k} {(1- \frac{1}{ p_i})}=ni=1k(1pi1)
=n∗p1−1p1∗p2−1p2∗p3−1p3⋯pk−1pk\hspace{3em}= n * {\frac{p_1 -1}{p_1}} * {\frac{p_2 -1}{p_2}} * {\frac{p_3 -1}{p_3}} \cdots {\frac{p_k -1}{p_k}}=np1p11p2p21p3p31pkpk1

最终我们发现欧拉函数只与它本身和其质因子有关,和质因子的次数无关,比如60,它的质因子为2,3,5
φ(60)=60∗2−12∗3−13∗5−15=16\varphi(60) = 60 * {\frac{2-1}{2}} *{\frac{3-1}{3}} *{\frac{5-1}{5}} = 16φ(60)=60221331551=16
所以就可以使用求质因数求出来后,算出n的欧拉函数

筛法求1∼n1 \sim n1n的欧拉函数

如果 i是质数,那么phi[i]=i−1phi[i]=i-1phi[i]=i1
如果是合数m,我们找到m的最小质因子p,就有m=p∗jm = p*jm=pj
此时分两种情况

  • j会被p整除,则j会包含了m的所有质因子,只是质因子p的指数不一样
    我们知道φ(m)=m∗∏i=1k(1−1pi)=p∗j∗∏i=1k(1−1pi)=p∗φ(j)\varphi(m)= m * {\prod_{i=1}^k} {(1- \frac{1}{ p_i})} =p*j *{\prod_{i=1}^k}{(1- \frac{1}{ p_i})} = p*\varphi(j)φ(m)=mi=1k(1pi1)=pji=1k(1pi1)=pφ(j)
  • j不会被p整除,那么它们两个互质。所以φ(m)=φ(p∗j)=φ(p)∗φ(j)\varphi(m) = \varphi(p*j)=\varphi(p)*\varphi(j)φ(m)=φ(pj)=φ(p)φ(j)

所以在用线性筛(欧拉筛)的时候我们已经记录了每个i的最小质因子,所以可以利用欧拉筛求出1∼n1 \sim n1n的欧拉函数

欧拉函数参考代码

#include
using namespace std;
using ll =long long ;

const int N=1e6;
int p[N],v[N];
int cnt;
int phi[N];

int getPhi(int n){
//	求n的欧拉函数
	int 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);
	return res;
}

void get_Phi(int n){
//	求1-n的欧拉函数
	phi[1]=1;
	for(int i=2;i<=n;i++){
		if(!v[i]){
			v[i]=i;
			phi[i]=i-1;
			p[++cnt]=i;
		}
		for(int j=1;j<=cnt;j++){
			if(p[j]>i||p[j]>n/i)break;
			int m=i*p[j];
			v[m]=p[j];
			if(i%p[j]==0){
				phi[m]=p[j]*phi[i];
			}
			else{
				phi[m]=phi[j]*phi[i];
			}
		}
	}
}
int main(){
	int n;
	cin>>n;
//	int phi=getPhi(n);
	get_Phi(n);
	for(int i=1;i<=n;i++)
		cout<<phi[i]<<' ';
	cout<<endl;
}

你可能感兴趣的:(算法,算法)