线性筛求质数
埃氏筛的缺陷:
对于一个数,埃氏筛会把6在2×3和3×2都删一次,导致时间复杂度达到了 O ( n l o g l o g n ) O(nloglogn) O(nloglogn),线性筛就是针对这个重复删除进行了优化,使得时间复杂度达到了线性的复杂度。
线性筛的优化过程:
显然,一个数(1除外)的倍数肯定是合数,这是我们从埃氏筛中就用的方法。
具体解释一下循环中的判断语句:
1.如果这个数是个质数,那么就加入到质数集合primenum[]中
2.如果这个数是个合数,根据唯一分解定理,
X
=
p
1
k
1
p
2
k
2
p
3
k
3
…
…
,
X=p_1^{k_1}p_2^{k_2}p_3^{k_3}……,
X=p1k1p2k2p3k3……,其中
p
i
p_i
pi都按升序排列
那么如果X一旦碰到了自己的一个质因子,即(X%primenum[j]==0),首先说明这个数肯定不是质数,其次是接下来的
i
∗
p
r
i
m
e
n
u
m
[
j
+
1
]
i*primenum[j+1]
i∗primenum[j+1]不用接着筛了,直接break,具体看证明:
个人证明:
步骤0:首先要明确 p r i m e n u m [ ] 是 递 增 的 primenum[]是递增的 primenum[]是递增的
步骤1:当一个数是质数的时候,就会加入到质数集合
步骤2:当一个数是合数的时候,什么时候要对它的倍数进行筛,什么时候不用呢?这就变成了最大的问题了!!观察分解后的式子,如果
i
%
最
小
质
因
子
=
=
0
i\%最小质因子==0
i%最小质因子==0,即说明这个数字肯定会被后续筛除,因为这个在maxn范围内最小质因子的所有倍数最终都会筛除,这个时候就需要用到步骤0了!!具体看式子:
i
%
p
r
i
m
e
n
u
m
[
j
]
=
=
0
i\%primenum[j]==0
i%primenum[j]==0==>设
i
=
k
∗
p
r
i
m
e
n
u
m
[
j
]
i=k*primenum[j]
i=k∗primenum[j];
i
∗
p
r
i
m
e
n
u
m
[
j
+
1
]
=
k
∗
p
r
i
m
e
n
u
m
[
j
]
∗
p
r
i
m
e
n
u
m
[
j
+
1
]
=
k
1
∗
p
r
i
m
e
n
u
m
[
j
]
i*primenum[j+1]=k*primenum[j]*primenum[j+1]\\=k_1*primenum[j]
i∗primenum[j+1]=k∗primenum[j]∗primenum[j+1]=k1∗primenum[j],也就是说在之后某个数字
k
1
k_1
k1乘上primenum[j]会筛选掉i×primenum[j+1]这个数字,如果之前筛了那就重复了
大神的图片解释:
参考:https://www.jianshu.com/p/f16d318efe9b
追究其源头的话,其实就是通过最小质因子达到筛选过程,例如6的最小质因子是2,那么就不必在3×2的地方进行操作了,这样的一个过程就是线性复杂度的
具体代码(板子:
时间复杂度: O ( n ) O(n) O(n)
bool prime[maxn];
int m=1;
int primenum[maxn];
void prime_init()
{
memset(prime,true,sizeof prime);
prime[0]=prime[1]=false;
rep(i,2,maxn)
{
if (prime[i])primenum[m++]=i;
for (int j=1;j<m&&i*primenum[j]<=maxn;j++)
{
prime[i*primenum[j]]=false;
if (i%primenum[j]==0)break;
}
}
}
例题1
HDU - 2161
题意:1和2不算质数,给出一组数据,判断是不是质数
做法:用线性筛的话,得往质数集里面添加一个2,尽管2在这个问题里不算质数,但是质数集里仍需要,因为考虑到4会被遗漏掉
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<queue>
#include<map>
#define ll long long
#define pb push_back
#define rep(x,a,b) for (int x=a;x<=b;x++)
#define repp(x,a,b) for (int x=a;x<b;x++)
#define W(x) printf("%d\n",x)
#define WW(x) printf("%lld\n",x)
#define pi 3.14159265358979323846
#define mem(a,x) memset(a,x,sizeof a)
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
using namespace std;
const int maxn=2e4+7;
const int INF=1e9;
const ll INFF=1e18;
int prime[maxn];
int primenum[maxn];
void prime_init()
{
int m=1;
rep(i,3,maxn)prime[i]=1;
prime[0]=prime[1]=prime[2]=0;
primenum[0]=2;
rep(i,2,maxn)
{
if (prime[i])
{
primenum[m++]=i;
}
for (int j=0;j<m&&i*primenum[j]<maxn;j++)
{
prime[i*primenum[j]]=0;
if (i%primenum[j]==0)break;
}
}
}
int main()
{
int n,K=0;
prime_init();
while(~scanf("%d",&n)&&n>0)
{
if (prime[n])printf("%d: yes\n",++K);
else printf("%d: no\n",++K);
}
}