质数筛也是数论中非常重要的一个部分。
根号筛
这个是很多人第一个学的筛法,也是最简单的筛法。
做法很简单,就是枚举 到 里所有的数,如果当前数可以被 整除,那么 显然就是一个和数。直到跑完循环也没返回,就说明 是一个指数。当然, 要特判, 也要特判。这样做每次判断一个数的时间复杂度是 ,判断 个数的时间复杂度是 。太慢了,要优化。
发现每当枚举的数 时,这个数就已经被判断过了,所以只需要枚举 到 的数就可以了,判断一个数的时间复杂度是 ,判断 个数的时间复杂度是 。
注意点
1.注意循环到 时,不建议直接用
code
sqrt(n)code
i*i<=ncode
i<=n/icode
这里只给出判断的函数。
cppbool isprime(int x){
if(x<2)return 0;
if(x==2)return 1;
for(int i=2;i<=x/i;i++){
if(x%i==0)return 0;
}
return 1;
}
埃筛
埃筛,也叫埃氏筛,全称埃拉托斯特尼筛法。是一种高效的判断质数的筛法。
原理是这样的:因为每一个的倍数都是合数,所以可以从 开始筛,如果当前的数标记为质数,就把它的倍数标成合数,如果是合数则直接跳过。埃筛的数也只需要枚举到 。 最后筛出来的标记数组就是每个数是质数还是合数的情况。 筛 个数的时间复杂度是 ,我也不知道怎么证。
值得一提的是,因为埃筛只用一个
code
bool code
bitsetcode
boolcode
bitset<N>primecode
bitset<int>prime[N]code
bitset<bool>prime[N]code
如果使用
code
boolcode
bitset<N>prime;code
bool prime[N];cpp#include<bits/stdc++.h>
using namespace std;
const int N=1e8+10;
int n,q;
bitset<N>prime;
int primes;
int cnt=0;
void init(int n){
for(int i=2;i<=n/i;i++){
if(prime[i])continue;
for(int j=i+i;j<=n;j+=i){
prime[j]=1;
}
}
for(int i=2;i<=n;i++){
if(!prime[i])cnt++;
}
cout<<cnt;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n;
init(n);
return 0;
}
线性筛
线性筛又名欧拉筛。顾名思义,它的时间复杂度是线性的,也就是 的。其实就是一种基于埃筛的优化。
当我们使用埃筛的时候,会发现,有一些数被重复筛了,比如 就同时被 和 筛,这样多余的操作会使程序变慢,而线性筛是每个数只筛 遍的。
操作如下: 用一个
code
primescode
boolcode
stcode
primescode
primescode
primes[j]*icode
primes[j]code
breakcode
primes[j]code
primescode
cpp#include<bits/stdc++.h>
using namespace std;
const int N=1e8+10;
int n,q;
int primes[N],cnt;
bool st[N];
void get_primes(int n){
for(int i=2;i<=n;i++){
if(!st[i])primes[cnt++]=i;
for(int j=0;primes[j]<=n/i;j++){
st[primes[j]*i]=true;
if(i%primes[j]==0)break;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
cin>>n>>q;
get_primes(n);
while(q--){
int k;
cin>>k;
cout<<primes[k-1]<<'\n';
}
return 0;
}
