【OI】線性篩

如何查找一個範圍內的全部素數?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] 的事情交給以後的質數序列去作。這樣,就能夠保證每一個質數只被篩一次。

 

最後輸出質數序列,相信各位都明白。

相關文章
相關標籤/搜索