问题描述:给定一个整数 N,求出 1~N 之间的所有质数,称为质数的筛选问题。
Eratothenes筛法:任意整数 x 的倍数 2x,3x,...都不是质数。我们从 2 开始,由小到大扫描每个数 x,把它的倍数
2x,3x,...[ N / x]*x 标记为合数。当扫描到一个数时,若它尚未标记,则它不能被 2 ~ x-1 之间的任何数整除,该数就是质数。实际上,小于 x^2 的 x 的倍数在扫描更小的数时就已经被标记过了。因此,对于每个数 x ,我们只需要从 x^2 开始,把 x^2,(x+1)*x,...,[ N / x] * x 标记为合数即可。时间复杂度:O(N*loglogN)。
代码实现:
void primes(int n){
memset(vis,0,sizeof vis);//合数标记
for(int i = 2; i <= n; i++){
if(vis[i]) continue;
printf("%d\n", i);//i是素数
for(int j = i; j <= n/i; j++)
vis[i*j]=1;
}
}
线性筛法:这个方法主要就是用已知的质数去筛出合数。任意一个合数都可以被质因数分解,那么我们就可以用已知的一个素数去筛选出一个以这个质数为最小质因数的合数。按照这个思想,我们在在生成一个需要标记的合数时,每次只想现有的数中乘上一个质因子,并且让它是这个合数的最小质因子。这相当于让合数的质因子从大到小累积,即让 12 只有 3*2*2一种产生方式。每个合数只会被它的最小质因子筛一次,时间复杂度为 O(N)。
int v[maxn];
int prime[maxn],m;
void primes(int n){
memset(v, 0, sizeof v);//最小质因子
m = 0;//质数数量
for(int i = 2; i <= n; i++){
if(v[i] == 0){//i是质数
v[i] = i;
prime[++m] = i;
}
//给当前的数 i 乘上一个质因子
for(int j = 1; j <= m; j++){
// i 有比 prime[j] 更小的质因子,或者超出 n 的范围。
if(prime[j] > v[i] || prime[j] > n / i)
break;
// prime[j] 是 i*prime[j] 的最小质因子
v[i*prime[j]] = prime[j];
}
}
for(int i = 1; i <= m; i++)
printf("%d\n", prime[i]);
}