淺談尤拉函式

Atserckcn發表於2024-09-01

尤拉函式

定義

對於任意的正整數 \(n\),尤拉函式 \(\phi(n)\) 表示小於等於 \(n\) 的所有數中與 \(n\) 互質的數的個數。

暴力實現

那麼根據定義,不難直接打出一個時間複雜度 \(O(n)\) 的程式碼,列舉所有小等於 \(n\) 的數字 \(i\),若 \(\gcd(n,i)=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;
}

很顯然,這段程式碼效率不夠高。有沒有什麼可以最佳化的地方?

尤拉函式推導

先根據定義可知以下幾點:

  • \(\phi(1)=1\)
  • \(n\) 為質數,則 \(\phi(n)=n-1\),即除了 \(n\) 自己外全都算。
  • \(n=p^k\),則 \(\phi(n)=p^k-p^{k-1}\)。因為只有一個數不含質數 \(p\),才可以與 \(n\) 互質。所以就要剪掉包含質數 \(p\) 的那 \(p^{k-1}\) 個數字。而 \(p^k-p^{k-1}=p^k(1-\frac{1}{p})=p^k\frac{p-1}{p}\)
  • \(n=ab\),其中 \(a,b\) 互質,則 \(\phi(n)=\phi(a)\times\phi(b)=(a-1)\times(b-1)\)。反過來也一樣,\(\phi(ab)=\phi(a)\times\phi(b)\)
  • \(n=p_1^{k_1}p_2^{k_2}\cdots p_i^{k_i}\cdots p_m^{k_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}\)

再把 \(p_1^{k_1}p_2^{k_2}\cdots p_m^{k_m}\) 拎出來,發現正好等於 \(n\),所以結果就變成了:

\[\phi(n)=n(\frac{p_1-1}{p_1})(\frac{p_2-1}{p_2})\cdots (\frac{p_m-1}{p_m}) \]

根據這個公式,我們就可以列舉 \(p_i\) 去求解 \(\phi(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;
}

相關文章