埃氏筛
简单,暴力。
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;
}
还是得多去复习。