埃氏篩
簡單,暴力。
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;
}
還是得多去複習。