概念梳理:
歐拉函數是少於或等於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)。
1 int oula(int n) 2 { 3 int rea=n; 4 for(int i=2; i<=n; i++) 5 if(n%i==0)//第一次找到的必為素因子 6 { 7 rea=rea-rea/i; 8 do 9 n/=i;//把該素因子全部約掉 10 while(n%i==0); 11 } 12 return rea; 13 }
這個函數的復雜度為O(n),如果n達到1000000000,肯定會超時,由於任何一個合數都至少有一個不大於根號n的素因子,所以只需遍歷到根號n即可,這樣復雜度降為O(√¯n)
下面是優化代碼:
1 int oula(int n) 2 { 3 int rea=n; 4 for(int i=2; i*i<=n; i++) 5 if(n%i==0)//第一次找到的必為素因子 6 { 7 rea=rea-rea/i; 8 do 9 n/=i;//把該素因子全部約掉 10 while(n%i==0); 11 } 12 if(n>1) 13 rea=rea-rea/n; 14 return rea; 15 }
(2)素數表實現
先把50 000以內的素數用篩選法選出來並保存,以方便歐拉函數使用,這樣,在不考慮篩選法的時間復雜度,而單純看歐拉函數,其復雜度為O(x),x為O(√¯n)以內素數的個數。
1 bool boo[50000]; 2 int p[20000]; 3 void prim() 4 { 5 memset(boo,0,sizeof(boo)); 6 boo[0]=boo[1]=1; 7 int k=0; 8 for(int i=2; i<50000; i++) 9 { 10 if(!boo[i]) 11 p[k++]=i; 12 for(int j=0; j<k&&i*p[j]<50000; j++) 13 { 14 boo[i*p[j]=1; 15 if(!(i%p[j])) 16 break; 17 } 18 } 19 }//篩選法打表 20 int phi(int n) 21 { 22 int rea=n; 23 for(int i=0; p[i]*p[i]<=n; i++)//對於一些不是素數的可不遍歷 24 if(n%p[i]==0) 25 { 26 rea=rea-rea/n; 27 do 28 n/=p[i]; 29 while(n%p[i]==0); 30 } 31 if(n>1) 32 rea=rea-rea/n; 33 return rea; 34 }
(3)遞推求歐拉函數
如果頻繁的使用歐拉函數值,就需要預先打表,下面介紹遞推求歐拉公式的方法。
可預先之所有數的歐拉函數值都為她本身,有定理可知,如果p是一個正整數且滿足φ(p)=p-1;那么p是素數,在遍歷過程中如果遇到歐拉函數與自身相等的情況。那么說明該數為素數,把這個數的歐拉函數值改變,同時也把能被素因子整除的數改變。
1 for(i=1; i<=maxn; i++) 2 p[i]=i; 3 for(i=2; i<=maxn; i+=2) 4 p[i]/=2; 5 for(i=3; i<=maxn; i+=2) 6 if(p[i]==i) 7 { 8 for(j=i; j<=maxn; j+=i) 9 p[j]=p[j]/i*(i-1); 10 }
噶嗚~附上歐拉函數表: