线性筛质数
暴力
讲解
对于每个数,我们用\(O(\sqrt n)\)的时间判断是否是质数
所以时间复杂度是\(O(n\sqrt n)\)
代码就不给出了
埃式筛法
讲解
一个很简单的筛法
对于每个质数,将它所有的倍数打上标记,表示不是质数
然后就可以做到\(O(n\log\logn)\)筛质数了
代码
bool vis[MAXN];
int prime[MAXN],pn;
void sieve(int x)
{
for(int i = 2;i <= x;++ i)
{
if(!vis[i])
{
prime[++pn] = i;
for(int j = i*2;j <= x;j += i)
vis[j] = 1;
}
}
}
欧式筛法
讲解
由于埃式筛法每个合数会被所有的质因数筛一遍,所以时间复杂度是\(O(nlogn)\)的
但是欧式筛法保证每个合数只会被最小的质因数筛一遍,从而保证了\(O(n)\)的时间复杂度
它是这么实现的(配代码食用更佳):
首先外层循环从\(2\)开始枚举倍数\(i\),如果发现当前数字不是质数,加入质数的集合
内层循环枚举已经筛出来的质数\(p_j\),将\(i*p_j\)打上标记,如果\(i\)是\(p_j\)的倍数,直接跳出循环
为什么可以直接跳出循环呢?
令\(j'>j\),所以\(p_{j'}>p_j\),因为\(i\)是\(p_j\)的倍数,所以\(i*p_{j'}\)也是\(p_j\)的倍数,它一定会被\(p_j\)筛掉
我们希望每个合数被最小的质因数筛掉,而\(p_j<p_{j'}\),所以可以直接跳出循环
代码
bool vis[MAXN];
int prime[MAXN],pn;
void sieve(int x)
{
for(int i = 2;i <= x;++ i)
{
if(!vis[i]) prime[++pn] = i;
for(int j = 1;j <= pn && i * prime[j] <= x;++ j)
{
vis[i * prime[j]] = 1;
if(i % prime[j] == 0)//超强优化!
break;
}
}
}