標籤:style blog http color strong os
題目:給出一個正整數n,列印出所有從1~n的素數(即質數);
關鍵是要找出一個判斷一個正整數n是否為素數的方法...
傻瓜解法--n,n/2
1 #include<stdio.h> 2 int main() 3 { 4 int i,n; 5 while(scanf("%d",&n)==1) 6 { for(i=2;i<n;i++) 7 if(n%i==0) break; 8 if(i==n) printf("YES\n"); 9 else printf("NO\n");10 }11 }
這是理所當然的想法,按照素數的定義,除了1和它本身沒有其他的因數,就是素數。
這種解法的缺點就是紅色標註那裡,i<n,或者有的是i<n....這種迴圈規模n稍微大點,已耗用時間不談了,都是淚...
普通解法--sqrt(n)
1 #include<stdio.h> 2 #include<math.h> 3 int main() 4 { int i,n,x; 5 while(scanf("%d",&n)==1) 6 { x=(int)sqrt(n); 7 for(i=2;i<=x;i++) 8 if(n%i==0) break; 9 if(i>x) printf("YES\n");10 else printf("NO\n");11 }12 }
這裡迴圈取到sqrt(n),效率改進不少了...但顯然還是不夠理想....繼續往下看
普通篩選法--埃拉托斯特尼篩法
先簡單說一下原理:
基本思想:素數的倍數一定不是素數
實現方法:用一個長度為N+1的數組儲存資訊(0表示素數,1表示非素數),先假設所有的數都是素數(初始化為0),從第一個素數2開始,把2的倍數都標記為非素數(置為1),一直到大於N;然後進行下一趟,找到2後面的下一個素數3,進行同樣的處理,直到最後,數組中依然為0的數即為素數。
說明:整數1特殊處理即可。
舉個例子,N=20時,示範如:
最後數組裡面還是0的就是素數了...
代碼實現如下:
prime[]用來儲存得到的素數 prime[] = {2,3,5,7,11,.........} tot 是當前得到的素數的個數 check :0表示是素數 1表示合數
1 memset(check, 0, sizeof(check)); 2 int tot = 0; 3 for (int i = 2; i <= n; ++i) 4 { 5 if (!check[i]) 6 { 7 prime[tot++] = i; 8 } 9 for (int j = i+i; j <= n; j += i)10 {11 check[j] = 1;12 }13 }
此篩選法的時間複雜度是O(nloglogn) 空間複雜度是O(n)
不足之處也比較明顯,手動類比一遍就會發現,很多數被處理了不止1遍,比如6,在素數為2的時候處理1次,為3時候又標記一次,因此又造成了比較大的不必要處理...那有沒有改進的辦法呢...就是下面改進之後的篩法...
線性篩法--歐拉篩法
先上代碼吧...
1 #include<cstdio> 2 #include<cstring> 3 #define MAXN 100005 4 #define MAXL 1299710 5 int prime[MAXN]; 6 int check[MAXL]; 7 8 int tot = 0; 9 memset(check, 0, sizeof(check));10 for (int i = 2; i < MAXL; ++i)11 {12 if (!check[i])13 {14 prime[tot++] = i;15 }16 for (int j = 0; j < tot; ++j)17 {18 if (i * prime[j] > MAXL)19 {20 break;21 }22 check[i*prime[j]] = 1;23 if (i % prime[j] == 0)24 {25 break;26 }27 }28 }
精華就在於紅色標註那兩處,它們保證每個合數只會被它的最小質因數篩去,因此每個數只會被標記一次,所以時間複雜度是O(n)
還是按上面的例子進行一遍類比:N=20
此過程中保證了兩點:
1、合數一定被幹掉了...
2、每個數都沒有被重複地刪掉
(證明 見參考資料)
引申--求歐拉函數
在數論,對正整數n,歐拉函數是小於或等於n的數中與n互質的數的數目。此函數以其首名研究者歐拉命名,它又稱為Euler‘s totient function、φ函數、歐拉商數等。 例如φ(8)=4,因為1,3,5,7均和8互質。
求歐拉函數的方法只需在上面的程式中稍有改動即可,此處只貼出代碼:
1 #include<cstdio> 2 #include<cstring> 3 #define MAXN 100005 4 #define MAXL 1299710 5 int prime[MAXN]; 6 int check[MAXL]; 7 int phi[MAXL]; 8 int tot = 0; 9 phi[1] = 1;10 memset(check, 0, sizeof(check));11 for (int i = 2; i < MAXL; ++i)12 {13 if (!check[i])14 {15 prime[tot++] = i;16 phi[i] = i - 1;17 }18 for (int j = 0; j < tot; ++j)19 {20 if (i * prime[j] > MAXL)21 {22 break;23 }24 check[i*prime[j]] = 1;25 if (i % prime[j] == 0)26 {27 phi[i*prime[j]] = phi[i] * prime[j];28 break;29 }else30 {31 phi[i*prime[j]] = phi[i] * (prime[j]-1);32 }33 }34 }
若是素數,那麼從1~n-1都是和它互質的數,所以phi(i) = i - 1;
另外兩個是積性函數(見參考資料)的公式和歐拉函數的特性。
參考資料
1、http://suno.cnblogs.com/
2、http://wenku.baidu.com/view/1187eebce009581b6ad9eb12
3、http://blog.csdn.net/dinosoft/article/details/5829550