暴力
讲解
对于每个数,我们用(O(sqrt n))的时间判断是否是质数
所以时间复杂度是(O(nsqrt n))
代码就不给出了
埃式筛法
讲解
一个很简单的筛法
对于每个质数,将它所有的倍数打上标记,表示不是质数
然后就可以做到(O(nlogn))筛质数了
代码
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;
}
}
}