线性筛算法思想
- 用最小素因数和一个整数去标记这个合数;
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 的最小因子。
线性筛代码实现
#include<stdio.h>
#define MAX_N 200000
int prime[MAX_N+5];
void init(){
for (int i =2 ; i <= MAX_N; i++) {
if (!prime[i]) prime[++prime[0]] = i;//将第i个素数存在prime[i]中;
for (int j = 1 ; j <= prime[0]; ++j) {
if (prime[j]*i > MAX_N) break;
prime[prime[j]*i] = 1;//将合数标记为1;
if (i % prime[j] == 0) break;//i相当于M;prime[j]最小素因数;
}
}
return ;
}
int main() {
init();
printf("%d\n", prime[10001]);
return 0;
}
关键之处在:if(i%prime[j]==0) break;
这句代码保证了每个数最多被筛一次,将时间复杂度降到了线性。
证:prime[]数组中的素数是递增的,当i能整除prime[j],那么iprime[j+1]这个合数肯定会被prime[j]乘以某个数筛掉。因此,这里直接break掉,将iprime[j+1]及之后的给后面的数去筛。这种方法能保证每个数只被筛一遍,又能保证每个数都被筛到。
- 为了更好的理解,画出前面几次筛的情况:
从图上我们看到,第一列筛掉的是最小素因子是2的数,第二列筛掉的是最小素因子为3的数,依次类推,可以把所有的合数都筛掉。
因为是按照最小素因子筛选,每个数的最小素因数只有一个,所以可以保证每个数都只会被筛一遍。
例如,i=6 时,第一个素数是2,能整除,筛掉12后就break;至于第二个素数3,6x3中的最小素因数肯定是前一个素数2,所以它要到 i=9,素数取2时才被筛掉。