n的歐拉函數值用符號φ(n)表示
歐拉函數的定義是,對於一個正整數n,小於n且與n互質的數的數目(包括1,特殊地,φ(1)=1 )。
設p1,p2,p3,...,pr為n的全部r個質因數,則有φ(n)=n*(1-1/p1)*(1-1/p2)*(1-1/p3)*(1-1/p4)…..(1-1/pr)。
顯然,用這個方法來計算單個歐拉函數是可以求解的。
附上代碼:
1 int get_phi(int x){ 2 int re=x; 3 for(int i=2;i*i<=x;i++) 4 if(x%i==0){ 5 re/=i;re*=i-1; 6 while(x%i==0) 7 x/=i; 8 } 9 //仍有質因數?特判x=2及x=3的情況 10 if(x^1) re/=x,re*=x-1; 11 return re; 12 }
這個求法的時間復雜度是O(√n)d的。
但這個求法有弊端:
* 對於大量需要計算的歐拉函數值,逐一分解n的時間復雜度顯然是十分高的。
在使用求解歐拉函數之前,介紹歐拉函數的幾個性質:
(1) 歐拉函數是積性函數,當正整數m,n互質時,滿足φ(mn)=φ(m)φ(n)。
(2) 當n為奇數時,有φ(2n)=φ(n)。
(3) n=∑ d|n φ(d) ( d|n )。
(4) 對於給定的一個質數p,φ(p)=p -1。則對於正整數 n = p^k ,φ(n) = p^k - p^(k -1)。
(5) 若(N % a == 0 && (N / a) mod a == 0) 則有:φ(N)=φ(N / a) * a。
(6) 若(N % a == 0 && (N / a) mod a != 0) 則有:φ(N)=φ(N / a) * (a - 1)。 (a 為質數)
主要利用以上性質(4)(5)(6),我們可以用遞推的方法按以下步驟來打出歐拉函數表。
(1) 記φ(1)=1。這是遞推步驟嗎。
(2) 根據性質(4),對於質數i,可直接得φ(i)=i-1,把i放入質數表prime中。
(3) 對於i,從頭開始枚舉質數(思考為什么枚舉質數),根據性質(5)(6),當(i mod primej) != 0時,φ(i*primej) = φ(i)*(primej -1)
當枚舉到(i mod primej) == 0時,φ(i*primej) = φ(i)*(primej),並停止(思考為什么要停止),枚舉下一個i。
由此我們得到了打出歐拉函數表的主要代碼(變量的定義不在這里展示)
1 phi[1]=1; 2 for(int i=2;i<=n;i++){ 3 if(!check[i]){ 4 prime[++cnt]=i; 5 phi[i]=i-1; 6 } 7 for(int j=1;j<=cnt&&prime[j]*i<=n;j++){ 8 check[i*prime[j]]=1; 9 if(i%prime[j]) phi[i*prime[j]]=phi[i]*(prime[j]-1); 10 else{ 11 phi[i*prime[j]]=phi[i]*prime[j]; 12 break; 13 } 14 } 15 }
可以簡單地得知時間復雜度是線性的O(n)。
利用打好的歐拉函數表,我們可以驗證它的以上幾個性質。
性質(2)
1 for(int i=n/2*2;i>1;i-=2) 2 if((i/2)&1) 3 printf("phi[%d] = %d = phi[%d/2] = phi[%d] = %d\n",i,phi[i],i,i/2,phi[i/2]);
example
phi[38] = 18 = phi[38/2] = phi[19] = 18
phi[34] = 16 = phi[34/2] = phi[17] = 16
phi[30] = 8 = phi[30/2] = phi[15] = 8
phi[26] = 12 = phi[26/2] = phi[13] = 12
phi[22] = 10 = phi[22/2] = phi[11] = 10
phi[18] = 6 = phi[18/2] = phi[9] = 6
phi[14] = 6 = phi[14/2] = phi[7] = 6
phi[10] = 4 = phi[10/2] = phi[5] = 4
phi[6] = 2 = phi[6/2] = phi[3] = 2
phi[2] = 1 = phi[2/2] = phi[1] = 1
性質(3)
1 for(int i=1;i<=n;i++){ 2 printf("%d = ",i); 3 int temp=0; 4 int j; 5 for(j=1;j<=i;j++) 6 if(i%j==0){ 7 temp+=phi[j]; 8 printf("phi[%d]( =%d ) ",j,phi[j]); 9 break; 10 } 11 for(j=j+1;j<=i;j++) 12 if(i%j==0){ 13 temp+=phi[j]; 14 printf("+ phi[%d]( =%d ) ",j,phi[j]); 15 } 16 printf(" =%d\n",temp); 17 }
example
1 = phi[1]( =1 ) =1
2 = phi[1]( =1 ) + phi[2]( =1 ) =2
3 = phi[1]( =1 ) + phi[3]( =2 ) =3
4 = phi[1]( =1 ) + phi[2]( =1 ) + phi[4]( =2 ) =4
5 = phi[1]( =1 ) + phi[5]( =4 ) =5
6 = phi[1]( =1 ) + phi[2]( =1 ) + phi[3]( =2 ) + phi[6]( =2 ) =6
7 = phi[1]( =1 ) + phi[7]( =6 ) =7
8 = phi[1]( =1 ) + phi[2]( =1 ) + phi[4]( =2 ) + phi[8]( =4 ) =8
9 = phi[1]( =1 ) + phi[3]( =2 ) + phi[9]( =6 ) =9
10 = phi[1]( =1 ) + phi[2]( =1 ) + phi[5]( =4 ) + phi[10]( =4 ) =10