Loading [MathJax]/jax/output/CommonHTML/fonts/TeX/AMS-Regular.js

学习笔记--简化剩余系与欧拉函数φ( )

什么是简化剩余系?


所有0<n<=m,(n,m)=10<n<=m,(n,m)=1的n构成了模m的简化剩余系,简称缩系


记这样n的个数为ϕ(m)ϕ(m)

相关性质

  • 如果(m,m)=1,a取遍模m缩系,a取遍m'缩系
    那么am+am取遍mm缩系
    - Prove:(a,m)=1,(a,m)=1,(m,m)=1
    (am,m)=1,(am,m)=1
    (am+am,m)=1,(am+am,m)=1 //加上另一个数的若干倍仍互质
    (am+am,mm)=1
    - 所以如果(n,m)=1,ϕ(nm)=ϕ(n)ϕ(m)

    • $\phi(pe)=(p-1)*p=p^e*(1-1/p) $ p是质数
      • Prove:[1,pe]ppe/p=pe1
        ϕ(pe)=pepe1=pe(11/p)
      • 特殊地,若p是一个质数,则ϕ(p)=p1

什么是欧拉函数


在数论,对正整数n,欧拉函数是小于或等于n的数中与n互质的数的数目。欧拉函数用希腊字母phi()ϕ() (念fai去声)表示,phi(n)表示正整数n的欧拉函数。


举个栗子:[1,12]中与12互质的有1,5,7,11
(别忘了,ab互质表示gcd(a,b)=1,故1也算)
所以ϕ(12)=4

很显然这个ϕ(m)就是上文简化剩余系中所提到的

欧拉函数的计算

由上文简化剩余系的性质可知

计算公式:ϕ(p)=ϕ(pcii)=(pci(11/pi))=n(11/pi)

计算方式

直接按照定义

int euler_phi(int n){
	int m=(int)sqrt(n+0.5);
	int ans=n;
	for(register int i=2;i<=m;i++)
		if(n%i==0)
		{//最好要先除后乘,防止结果溢出
		  ans=ans/i*(i-1);   //上文推导得 
		  while(n%i==0)n=n/i;//将n中所有因子i筛去 
		                   //确保下一个i是n的质因子 
		}
	if(n>1)ans=ans/n*(n-1);//防止n为最后一个质因子 
	return ans;
}

例题: hdu 1787裸欧拉函数简单变式

有没有O(N)预处理出一张欧拉函数表的方法呢?当然有,在欧拉筛的基础上稍加改动即可,想要看懂代码请您先熟练欧拉筛

这是一个欧拉筛

void Euler_Prime() 
{ 
  memset(is_Prime,1,sizeof(is_Prime));
  memset(pri,0,sizeof(pri)); 
  is_Prime[0]=0;
  is_Prime[1]=0;//特判
   for(register int i=2;i<=n;i++) { 
      if(is_Prime[i]) 
        pri[tot++]=i;   //----1
      for(register int j=0; j<tot && i*pri[j]<=n;j++){ 
         is_Prime[i*pri[j]]=0; 
         if(i%pri[j]==0) break; //-----2
         //-----3
      } 
   }  
} 

使用欧拉筛时无非在代码中1,2,3处三种情况:

(话说第二条的证明找了挺久,好多人都直接略过,感觉我真的太菜了

  1. 判定i是一个质数,根据上文性质ϕ(i)=i1

  2. 在2处,ϕ(ipri[j])=ϕ(i)pri[j]

    Prove:x=pri[j]i,易知此时i包含pri[j]这个质因子,即i的质因子与x的相同,根据欧拉函数的直接计算方式,

    ϕ(x)=x(11/pi)=pri[j]i(11/pi)=pri[j]ϕ(i)

  3. 在3处,易知ipri[j]互质,根据欧拉函数性质(也是积性函数性质)

    ϕ(x)=ϕ(i)ϕ(pri[j])=ϕ(i)(pri[j]1)

然后就可以看懂代码了

inline void get_phitable(){
	bool is_pri[maxn];
	int num[1000005],tot=0,tmp;
	memset(is_pri,0,sizeof(is_pri));
	is_pri[1]=1;
	phi[1]=1;
	for(ri i=2;i<=maxn;i++){
	    //printf("%d\n",i);
		if(!is_pri[i]){
			num[++tot]=i;
			phi[i]=i-1;
		}
		for(ri j=1;j<=tot;j++){
			tmp=num[j]*i;
			if(tmp>=maxn)break;
			is_pri[tmp]=1;
			if(i%num[j]==0){
				phi[tmp]=num[j]*phi[i];
				break;
			}
			else {
				phi[tmp]=(num[j]-1)*phi[i];
			}
		}
	}
	return ;
}

推荐阅读

posted @   Rye_Catcher  阅读(2120)  评论(0编辑  收藏  举报
编辑推荐:
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示