這學期的離散數學課程學了一點初等數論,其中的埃氏篩法當時課上沒有太懂,課后看了《挑戰程序設計競賽》一書終於弄懂了。(這本書確實很好!算法簡潔優美。)
如果只對一個整數進行素性測試,通常O(√n )的算法就足夠了。但如果要對許多整數進行素性測試,則有更為高效的算法,其中就包括埃拉托斯特尼篩法,簡稱埃氏篩法。它是一個與輾轉相除法一樣古老的算法,可以用於枚舉n以內的素數。
首先,我們將2到n范圍內的所有整數寫下來。其中最小的數字2是素數。將表中所有2的倍數都划去。表中剩余的最小數字是3,它不能被更小的數整除,所以是素數。再將表中所有3的倍數都划去。依此類推,如果表中剩余的最小數字是m時,m就是素數。然后將表中所有m的倍數都化去。像這樣反復操作,就能依次枚舉n以內的素數。
如圖所示,最終我們就能得到20以內的所有素數。埃氏篩法的復雜度僅有O(nlognlogn)。對於程序設計競賽中的數據規模,將它的復雜度看作大致線性的也無妨。下面給出代碼:
1 #include <iostream> 2 #include <cstring> 3 using namespace std; 4 5 //埃氏篩法 6 7 const int MAX_N = 10005; 8 int prime[MAX_N]; //第i個素數 9 bool is_prime[MAX_N+1]; //is_prime[i]為true時表示i是素數 10 11 //返回n以內素數的個數 12 int sieve(int n){ 13 int p = 0; 14 for(int i = 0; i <= n; i++) is_prime[i] = true; 15 is_prime[0] = is_prime[1] = false; 16 for(int i = 2; i <= n; i++){ 17 if(is_prime[i]){ 18 prime[p++] = i; 19 for(int j = 2*i; j <= n; j+=i) is_prime[j] = false; //篩去所有素數的倍數 20 } 21 } 22 return p; 23 } 24 25 26 int main() 27 { 28 int n; //枚舉n以內素數 29 while(cin>>n){ 30 int p = sieve(n); 31 cout<<p<<endl; 32 for(int i = 0; i < p;i++) 33 cout<< prime[i]<<" "; 34 cout<<endl; 35 } 36 37 return 0; 38 }