线性筛法求素数
Posted return-blog
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线性筛法求素数相关的知识,希望对你有一定的参考价值。
说到求素数,其实自己刚学C++那时也遇到过这种问题,如果你那时候叫我写个求1~100以内的素数的程序,我会毫不犹豫的这样写(然后被一堆大佬疯狂吊打qwq):
#include <iostream>
using namespace std;
bool prime(int x){
if(x<=1) return false;
for(int i=2;i*i<=x;i++){
if(x%i==0) return false;
}
return true;
}
int main(){ //求1~100以内的素数
for(int i=1;i<=100;i++)
if(prime(i)) cout << i << ' ';
return 0;
}
其实这并不算筛法,而且时间复杂度非常高,该算法比较少用, 但优点是占用空间较少 。
但如果题目的数据范围一大起来,这种算法肯定挂 (这也是自己被大佬们疯狂吊打的原因吧) 。
埃式筛
第一种算法的时间复杂度太高了,下面介绍一种真正的筛法,即埃式筛,而且时间复杂度还比上面介绍的低 (这不废话吗,比上面的高那还用介绍吗?) :
#include <iostream>
using namespace std;
bool book[20005];
int main(){ //求1~100以内的素数
for(int i=2;i<=100;i++)
if(!book[i]){
for(int j=i+i;j<=100;j+=i)
book[j]=1;
}
for(int i=2;i<=100;i++)
if(!book[i]) cout << i;
return 0;
}
以上代码的思想是:对于不超过n的素数p,删除2p,3p,4p…,当处理完所有的数之后,还没有被删除的数就都是素数。
怎么样,是不是比第一种方法快很多?
但是,仔细分析我们就会发现,这种筛法还是存在一个问题:重复筛,比如当i为2的时候,3i是等于6的,也就是说,当i=2的时候6就被筛掉了,但是i=3的时候,2i也等于6,我们会发现6又被筛了一次,所以这种筛法还是达不到我们的要求。
线性筛
现在轮到今天的主角登场了!这就是线性筛!那么线性筛到底有什么好的呢?
先来看看线性筛的思路:
其实,线性筛就是埃式筛的升级版,它解决了埃式筛遗留的问题:重复筛。它让每个合数只被他的最小质因子筛一遍,它的时间复杂的接近于O(N) (这么好的算法,简直是AK IOI必备啊)
上代码:
#include <iostream>
using namespace std;
bool book[20005];
int prime[20005],pos,i,j;
int main(){ //求1~100以内的素数
for(i=2;i<=100;i++){
if(!book[i]) prime[++pos]=i;
for(j=1;j<=pos;j++){
if(i*prime[j] > 100) break;
book[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
for(int i=1;i<=pos;i++)
cout << prime[pos];
return 0;
}
现在来逐行代码解析:
现在来看 if(!book[i]) prime[++pos]=i;
,这段代码的作用是判断当前数是否被筛过(如果没筛过,即book[i]=0,那么这个数就是素数,所以就要往prime的尾部添加这个数字),,
if(i*prime[j] > 100) break;
这段代码的作用是控制筛的范围
book[i*prime[j]]=1;
则是把当前的数筛掉
if(i%prime[j]==0) break;
接下来重点解析这句话,他就是避免重复筛的关键!
当i是prime[j]的整数倍时,说明 i * prime[j+1] 是 prime[j] 的整数倍,不需要现在筛出,因为在之后筛除过程中i * prime[j+1] 这个合数一定会被prime[j]筛除,prime[j]之后的所有素数同理,所以break跳出循环。
(参考文章:https://blog.csdn.net/FeilingGong/article/details/83660779)
以上是关于线性筛法求素数的主要内容,如果未能解决你的问题,请参考以下文章