如何查找一個範圍內的全部素數?ios
能夠是從1~n挨個判斷n%i 是否 == 0,也能夠從 1~sqr(n) 一個個判斷。數組
相信大家也據說過埃氏篩法,是使用每個數的倍數篩掉合數!可是!每個合數要被篩屢次!這就給了咱們優化的可乘之機!優化
它叫作線性篩,顧名思義,時間複雜度是線性的。blog
咱們都知道,線性的複雜度已經很是優秀了,接下來咱們的目的就是如何讓每個合數只被篩一次。io
下面記住這條線性篩的原理:class
重要的事情說三遍!!!stream
#include<iostream> #include<cstdio> const int MaxN = 20025; bool notp[MaxN]; int prime[MaxN]; int tot; int main(){ int x; scanf("%d",&x); for(int i = 2; i <= x; i++){ if(notp[i] == 0){ prime[++tot] = i; } for(int j = 1; j <= tot; j++){ if(i * prime[j] > x) break; notp[i*prime[j]] = 1; if(i % prime[j] == 0) break; } } for(int i = 1; i <= tot; i++){ printf("%d ",prime[i]); } return 0; }
看起來很是簡短,對不對?原理
下面解釋一下這段代碼:循環
輸入x,求0~x區間中的全部素數。im
接下來,是2~x的枚舉,爲何從2開始相信各位也都明白。
notp數組的意思是"is not a prime",不是一個質數,值爲0時表明它是一個質數。(在開始咱們已經把它默認設置爲全爲素數,儘管事實不是那樣)
若是發現一個數"is not not a prime"(=="is a prime)就把它放到咱們的素數數組裏,至於爲何這麼作稍後解答。(並且,咱們顯然能夠看出這個數組具備單調遞增的性質)
接下來咱們就循環咱們的素數數組,對於每一個元素篩去它的質數倍。
相信 if ( i * prime[j] > x) break; 你們都理解,下面就是標記它是一個素數。
接下來就是重點!
線性篩所有的Knowledge都凝聚在這一句話!if ( i % prime[j] == 0 ) break;
它意味着,只有最小的質因數才能篩去其它的數。
由於,若是 i % prime[j] == 0 的話,這就是說 i 擁有一個最小的質因數 prime[j] ,緣由有兩條:①咱們以前沒有觸發過 i % prime[j] == 0,那麼就是說,以前的 prime[1~j]都不是i的任何因數。
② i % prime[j] == 0 意味着, i * prime[j] 等同於 prime[j] * i/prime[j] * prime[j] ,其中全部數均爲整數,最小質因子就是 prime[j]。這違反了咱們在一開始的原理:
而咱們經過找規律發現,i * prime[j] 的最小質因子永遠是素數序列中的prime[j]。
(例如,素數序列是 2,3,正在枚舉4,4*2=8,最小質因子是2;素數序列是2,3,正在枚舉3,2*3=6,3*3=9,2和3都是其中的最小質因子)
那麼,接下來咱們要注意一個事情。爲何 if( i % prime[j] == 0 ) break; 要放在 notp[i * prime[j]] = 1; 後面呢?難道不能放在它前面嗎?
剛剛我解釋過的緣由①,在觸發 if( i % prime[j] == 0 ) break; 意味着prime[j]是i最早遇到的質因子,而且咱們知道某一個數的某個因子必定小於等於它自己,因此顯而易見,咱們的素數序列包括到i(不管i是不是質數)的全部質數(若是i是質數,那麼包括i)。咱們以前也說過這個質數序列是單調遞增的,那麼prime[j]就是 i 的最小質因子!
而咱們之因此在篩完 i * prime[j] 以後才運行這個條件判斷,是由於 i % prime[j] 第一次 == 0 的時候,prime[j]是i的最小質因子!若是這裏不break,那麼下一次 i % prime[k] == 0的時候,顯而易見,prime[k]不是i的最小質因子,這違反了咱們在開頭給出的原理。爲了不某些合數未來被篩第二次,因此咱們這裏直接break,把篩掉剩下 i * prime[x] 的事情交給以後的質數序列去作。這樣,就能夠保證每一個質數只被篩一次。
最後輸出質數序列,相信各位都明白。