埃拉托斯特尼篩法可以在 O(nloglogn)的復雜度內篩出素數,但事實上歐拉篩(線性篩)可以達到O(n)的線性效率!
先來看歐拉篩的算法及實現,然后再思考埃氏篩法時間都多在哪了。
歐拉篩算法步驟:
1.如果上界小於2,沒有素數,返回。
2.標記i=2為第一個素數。然后如果沒有到達上界,轉移到步驟3,否則轉移到步驟5。
3. 令i自增1。檢驗i是否是素數,如果是素數,將其填入素數數組中,轉移到步驟4。
4.,無論它是不是素數,都將它與目前已經找到的素數的乘積判定為合數。如果某一個素數可以被當前這個數 i 整除,或者所有已經選出的素數都已經遍歷過,或者當前素數*i后大於上界,轉移到步驟5。
5.到上界后,返回。否則轉移到步驟3。
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 void get_prime(vector<int> & prime,int upper_bound){ // 傳引用 5 if(upper_bound < 2)return; 6 vector<bool> Is_prime(upper_bound+1,true); 7 for(int i = 2; i <= upper_bound; i++){ 8 if(Is_prime[i]) 9 prime.push_back(i); 10 for(int j = 0; j < prime.size() and i * prime[j] <= upper_bound; j++){ 11 Is_prime[ i*prime[j] ] = false; 12 if(i % prime[j] == 0)break;// 保證了一個數只被篩一次。 13 } 14 } 15 } 16 int main(){ 17 vector<int> prime; 18 get_prime(prime, 10000001); 19 for(vector<int> :: iterator it = prime.begin(); it not_eq prime.end(); it++) 20 cout<<*it<<" "; 21 return 0; 22 }
可以看出,歐拉篩法和埃氏篩法的主要不同之處有兩點:
1.篩選方式不同:埃氏篩法是針對每一個素數z,一次性篩除z的所有倍數。而歐拉篩法則是一步步篩掉一個素數的倍數。
看看埃氏篩法的時間都多在哪了?比如數6,它被2、3重復篩除了兩次。數42,被2、3、7篩過三次。那么不妨將n個素數相乘,對於這個乘積,我們一共多篩了n-1次!如何避免這種多余的工作呢?看下一條。
2.歐式篩法的關鍵特別之處在於一條語句: if(i % prime[j] == 0)break; 這條語句保證了,每一個合數都只被篩除一次。為什么呢?由於 i % prime[i] == 0 ,那么如果繼續篩下去,i * prime[j+1]一定也會是某一個合數,那么如果下一次判斷這個合數的時候,它依然會被 prime[j] 篩掉。雖然prime[j+1] 也可以篩除它,不過它已經被prime[j]篩過了,prime[j+1]的篩除工作是多余的。