概念梳理:
歐拉函數是少於或等於n的數中與n互質的數的數目。
歐拉函數的性質:它在整數n上的值等於對n進行素因子分解後,所有的素數冪上的歐拉函數之積。
歐拉函數的值 通式:φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn),其中p1, p2……pn爲x的所有質因數,x是不爲0的整數。φ(1)=1(唯一和1互質的數(小於等 於1)就是1本身)。 (注意:每種質因數只一個。比如12=2*2*3那麼φ(12)=12*(1-1/2)*(1-1/3)=4)
推論:當n爲奇數時,有φ(2n)=φ(n)。
若n是質數p的k次冪,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1),因爲除了p的倍數外,其他數都跟n互質。
設n爲正整數,以 φ(n)表示不超過n且與n互素的正整數的個數,稱爲n的歐拉函數值,這裏函數φ:N→N,n→φ(n)稱爲歐拉函數。
歐拉函數是積性函數——若m,n互質,φ(mn)=φ(m)φ(n)。
特殊性質:當n爲奇數時,φ(2n)=φ(n), 證明與上述類似。
算法實現與分析:
求解歐拉函數的值可用φ(x)=x(1-1/p1)(1-1/p2)(1-1/p3)(1-1/p4)…..(1-1/pn),容易知道要對n進行素因子分解。
(1)直接實現 題目
int oula(int n)
{
int rea=n;
for(int i=2; i<=n; i++)
if(n%i==0)//第一次找到的必爲素因子
{
rea=rea/i*(i-1);
do
n/=i;//把該素因子全部約掉
while(n%i==0);
}
return rea;
}
這個函數的複雜度爲O(n),如果n達到1000000000,肯定會超時,由於任何一個合數都至少有一個不大於根號n的素因子,所以只需遍歷到根號n即可,這樣複雜度降爲O(√¯n)
下面是優化代碼:
int oula(int n)
{
int rea=n;
for(int i=2; i*i<=n; i++)
if(n%i==0)//第一次找到的必爲素因子
{
rea=rea/i*(i-1);
do
n/=i;//把該素因子全部約掉
while(n%i==0);
}
if(n>1)
rea=rea-rea/n;
return rea;
}
(2)素數表實現
先把50 000以內的素數用篩選法選出來並保存,以方便歐拉函數使用,這樣,在不考慮篩選法的時間複雜度,而單純看歐拉函數,其複雜度爲O(x),x爲O(√¯n)以內素數的個數。
bool boo[50000];
int p[20000];
void prim()
{
memset(boo,0,sizeof(boo));
boo[0]=boo[1]=1;
int k=0;
for(int i=2; i<50000; i++)
{
if(!boo[i])
p[k++]=i;
for(int j=0; j<k&&i*p[j]<50000; j++)
{
boo[i*p[j]=1;
if(!(i%p[j]))
break;
}
}
}//篩選法打表
int phi(int n)
{
int rea=n;
for(int i=0; p[i]*p[i]<=n; i++)//對於一些不是素數的可不遍歷
if(n%p[i]==0)
{
rea=rea-rea/n;
do
n/=p[i];
while(n%p[i]==0);
}
if(n>1)
rea=rea-rea/n;
return rea;
}
(3)遞推求歐拉函數 題目
如果頻繁的使用歐拉函數值,就需要預先打表,下面介紹遞推求歐拉公式的方法。
可預先之所有數的歐拉函數值都爲她本身,有定理可知,如果p是一個正整數且滿足φ(p)=p-1;那麼p是素數,在遍歷過程中如果遇到歐拉函數與自身相等的情況。那麼說明該數爲素數,把這個數的歐拉函數值改變,同時也把能被素因子整除的數改變。
for(i=1; i<=maxn; i++)
p[i]=i;
for(i=2; i<=maxn; i+=2)
p[i]/=2;
for(i=3; i<=maxn; i+=2)
if(p[i]==i)
{
for(j=i; j<=maxn; j+=i)
p[j]=p[j]/i*(i-1);
}