[算法日常] 浅谈欧拉函数

欧拉函数

定义

对于任意的正整数 n n n,欧拉函数 ϕ ( n ) \phi(n) ϕ(n) 表示小于等于 n n n 的所有数中与 n n n 互质的数的个数。

暴力实现

那么根据定义,不难直接打出一个时间复杂度 O ( n ) O(n) O(n) 的代码,枚举所有小等于 n n n 的数字 i i i,若 gcd ⁡ ( n , i ) = 1 \gcd(n,i)=1 gcd(n,i)=1 则答案 + 1 +1 +1

代码:

#include<bits/stdc++.h>
using namespace std;
int n,ans;
int gcd(int x,int y)
{
	return !y?x:gcd(y,x%y);
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		if(gcd(i,n)==1)
			ans++;
	printf("%d\n",ans);
	return 0;
}

很显然,这段代码效率不够高。有没有什么可以优化的地方?

欧拉函数推导

先根据定义可知以下几点:

  • ϕ ( 1 ) = 1 \phi(1)=1 ϕ(1)=1
  • n n n 为质数,则 ϕ ( n ) = n − 1 \phi(n)=n-1 ϕ(n)=n1,即除了 n n n 自己外全都算。
  • n = p k n=p^k n=pk,则 ϕ ( n ) = p k − p k − 1 \phi(n)=p^k-p^{k-1} ϕ(n)=pkpk1。因为只有一个数不含质数 p p p,才可以与 n n n 互质。所以就要剪掉包含质数 p p p 的那 p k − 1 p^{k-1} pk1 个数字。而 p k − p k − 1 = p k ( 1 − 1 p ) = p k p − 1 p p^k-p^{k-1}=p^k(1-\frac{1}{p})=p^k\frac{p-1}{p} pkpk1=pk(1p1)=pkpp1
  • n = a b n=ab n=ab,其中 a , b a,b a,b 互质,则 ϕ ( n ) = ϕ ( a ) × ϕ ( b ) = ( a − 1 ) × ( b − 1 ) \phi(n)=\phi(a)\times\phi(b)=(a-1)\times(b-1) ϕ(n)=ϕ(a)×ϕ(b)=(a1)×(b1)。反过来也一样, ϕ ( a b ) = ϕ ( a ) × ϕ ( b ) \phi(ab)=\phi(a)\times\phi(b) ϕ(ab)=ϕ(a)×ϕ(b)
  • n = p 1 k 1 p 2 k 2 ⋯ p i k i ⋯ p m k m n=p_1^{k_1}p_2^{k_2}\cdots p_i^{k_i}\cdots p_m^{k_m} n=p1k1p2k2pikipmkm,则根据第三点,可表示为 p 1 k 1 p 1 − 1 p 1 p 2 k 2 p 2 − 1 p 2 ⋯ p m k m p m − 1 p m p_1^{k_1}\frac{p_1-1}{p_1}p_2^{k_2}\frac{p_2-1}{p_2}\cdots p_m^{k_m}\frac{p_m-1}{p_m} p1k1p1p11p2k2p2p21pmkmpmpm1

再把 p 1 k 1 p 2 k 2 ⋯ p m k m p_1^{k_1}p_2^{k_2}\cdots p_m^{k_m} p1k1p2k2pmkm 拎出来,发现正好等于 n n n,所以结果就变成了:
ϕ ( n ) = n ( p 1 − 1 p 1 ) ( p 2 − 1 p 2 ) ⋯ ( p m − 1 p m ) \phi(n)=n(\frac{p_1-1}{p_1})(\frac{p_2-1}{p_2})\cdots (\frac{p_m-1}{p_m}) ϕ(n)=n(p1p11)(p2p21)(pmpm1)
根据这个公式,我们就可以枚举 p i p_i pi 去求解 ϕ ( n ) \phi(n) ϕ(n)。代码:

#include<bits/stdc++.h>
using namespace std;
int n,ans;
int main(){
	scanf("%d",&n);
	ans=n;
	if(n==1)//特判
	{
		printf("1\n");
		return 0;
	}
	for(int i=2;i*i<=n;i++)
	{
		if(n%i==0)
		{
			ans=ans*(i-1)/i;//公式
			while(!(n%i))//能整除就一直除下去
				n/=i;
		}
	}
	if(n>1)//如果n还没有除尽
		ans=ans*(n-1)/n;
	printf("%d\n",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值