埃氏篩&線性(歐拉)篩

埃氏篩

簡單,暴力。

int isprime[50000];
void getlist(int size) {
	memset(isprime, 1, sizeof(isprime));
	
	isprime[1] = 0;
	for(int i = 2; i <= size; i++) if(isprime[i]) 
	for(int j = 2; i*j <= size; j++) isprime[i*j] = 0; 
	
	return;
}

看似簡單,但不真搞明白這個,無法學會線性 ( 歐拉 ) 篩。

關鍵在於如何去做的合數,用了兩個參數,一個是質數,即 prime[ i ] ,另一個是質數的倍數,即 j 。相乘得到合數。

 

線性篩

埃氏篩的改良版,使一個合數只被做出來一次。

類比dfs的一些判重,用一定的順序( 如從小到大 )來去重。在這裏,我們便是保證每個合數只被最小的質因數做出來。

在線性篩裏,我們並不是先找質數,再用不同的倍數去乘它。我們遍歷倍數,用不同的質數去乘它。當某個質數是這個倍數的因數時,換下一個倍數。

爲什麼呢?

引用一段標準解釋:

prime[] 數組中的素數是遞增的,當i能整除 prime[ j ],那麼 i*prime[ j+1 ]這個合數肯定被 prime[ j ] 乘以某個數篩掉。

因爲i中含有 prime[ j ] , prime[ j ] 比 prime[ j+1 ]小,即 i = k*prime[ j ]。

那麼 i * prime[ j+1 ] = (k*prime[ j ]) * prime[ j+1 ] = k’ * prime[ j ],接下去的素數同理。所以不用篩下去了。

因此,在滿足 i%prime[ j ] == 0這個條件之前以及第一次滿足改條件時, prime[ j ] 必定是 prime[ j ] * i 的最小因子。

請看下面一個樣例,假設我們的倍數遍歷到了9,而去乘它的質數到了3。

遍歷的倍數     9

質數表 2  3  5  7

這時候我們就可以換下一個倍數10了,爲什麼呢?

假如我們繼續,用9*5得到45,請注意,因爲3是9的因數,所以3也是45的因數,且必定是45的最小質因數。因爲3是9的最小質因數,除非9乘的質因數小於3,否則45的最小質因數就是3。5在3的右邊,所以winner就是3了。那麼15就是造這個合數所需要的倍數,肯定大於9,會在後面遍歷到這個倍數。

所以在3*9後,我們可以換下一個倍數10了。

我將 i ,j 對換,來匹配埃氏篩的意義。 

int isprime[50000], prime[10000], tot;
void getlist(int size) {
	memset(isprime, 1, sizeof(isprime));
	isprime[1] = 0;
	for(int j = 2; j <= size; j++) {
		if(isprime[j]) prime[++tot] = j;
		for(int i = 1; i <= tot && j*prime[i] <= size; i++) {
			isprime[j*prime[i]] = 0;
			if(j%prime[i] == 0) break;
		}
	}
	return;
}

還是得多去複習。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章