轉自:http://blog.csdn.net/dinosoft/article/details/5829550ios
首先先介紹通常的線性篩法求素數算法
void make_prime() { memset(prime, 1, sizeof(prime)); prime[0]=false; prime[1]=false; int N=31700; for (int i=2; i<N; i++) if (prime[i]) { primes[++cnt ]=i; for (int k=i*i; k<N; k+=i) prime[k]=false; } return; }
這種方法比較好理解,初始時,假設所有都是素數,當找到一個素數時,顯然這個素數乘上另一個數以後都是合數(注意上面的 i*i , 比 i*2 要快點 ),把這些合數都篩掉,即算法名字的由來。spa
但仔細分析能發現,這種方法會形成重複篩除合數,影響效率。好比10,在i=2的時候,k=2*15篩了一次;在i=5,k=5*6 的時候又篩了一次。因此,也就有了快速線性篩法。.net
快速線性篩法沒有冗餘,不會重複篩除一個數,因此「幾乎」是線性的,雖然從代碼上分析,時間複雜度並非O(n)。先上代碼code
#include<iostream> using namespace std; const long N = 200000; long prime[N] = {0},num_prime = 0; int isNotPrime[N] = {1, 1};//先將0,1排除 int main() { for(long i = 2 ; i < N ; i ++) { if(! isNotPrime[i]) //isNotPrime==0 prime[num_prime ++]=i;//本身在前面打好了標記。 //關鍵處1 /*i爲合數時也要參與循環*/ for(long j = 0 ; j < num_prime/*小於已經求出的質數個數*/ && i * prime[j]/*乘以其它質數,且質數比本身小或者是本身(已經求出來了),因此不可能重複*/ < N ; j ++) { isNotPrime[i * prime[j]] = 1;//乘以其它素數獲得的必定是合數,之因此不重複是由於質數間不等 if( !(i % prime[j] ) ) //關鍵處2 i%prime==0 根據「關鍵處2」的定義,當p1==prime[j] 的時候, //篩除就終止了(從最小質數開始因此本句話成立), break; } } return 0; //咱們能夠直觀地舉個例子。i=2*3*5*3,補大 //此時能篩除 2*i ,不能篩除 3*i //若是能篩除3*i 的話,當 i' 等於 i'=3*3*5 加小 時,篩除2*i' 就和前面重複了。(順序性) }
先,先明確一個條件,任何合數都能表示成一系列素數的積。blog
無論 i 是不是素數,都會執行到「關鍵處1」,get
①若是 i 都是是素數的話,那簡單,一個大的素數 i 乘以不大於 i 的素數,這樣篩除的數跟以前的是不會重複的。篩出的數都是 N=p1*p2的形式, p1,p2之間不相等io
②若是 i 是合數,此時 i 能夠表示成遞增素數相乘 i=p1*p2*...*pn, pi都是素數(2<=i<=n), pi<=pj ( i<=j )class
p1是最小的係數。效率
根據「關鍵處2」的定義,當p1==prime[j] 的時候,篩除就終止了,也就是說,只能篩出不大於p1的質數*i。
咱們能夠直觀地舉個例子。i=2*3*5
此時能篩除 2*i ,不能篩除 3*i
若是能篩除3*i 的話,當 i' 等於 i'=3*3*5 時,篩除2*i' 就和前面重複了。
根據上面紅字的條件,如今分析一個數會不會被重複篩除。
設這個數爲 x=p1*p2*...*pn, pi都是素數(1<=i<=n) , pi<=pj ( i<=j )
當 i = 2 時,就是上面①的狀況,
當 i >2 時, 就是上面②的狀況, 對於 i ,第一個能知足篩除 x 的數 y 必然爲 y=p2*p3...*pn(p2能夠與p1相等或不等),並且知足條件的 y 有且只有一個。因此不會重複刪除。
證實合數確定會被幹掉? 用概括法吧。
類比一個模型,好比說咱們要找出 n 中2個不一樣的數的全部組合 { i , j } ,1<=i<=n, 1<=j<=n,
咱們會這麼寫
for (i=1; i<n; ++i )
for (j=i+1; j<=n; ++j)
{
/////
}
咱們取 j=i+1 便能保證組合不會重複。快速篩法大概也是這個道理,不過這裏比較難理解,沒那麼直觀。