素数也算是程序设计比赛中经常遇到的老对手了,对付它最基本的方法(这里说的是基本,不是蠢,怎么说我以前也一直在用)是通过素数的定义判断,只能被1和本身整除的数是素数。这种方法适合判断单个数是否为素数,当要求一个范围时,这种方法就基本上是T了。
这时我们就可以使用素数筛来做。
筛法的思想是去除要求范围内所有的合数,剩下的就是素数了,而任何合数都可以表示为素数的乘积,因此如果已知一个数为素数,则它的倍数都为合数。
先附上比较好理解的代码(粗略版)
#include"cstdio"
#include"cstring"
using namespace std;
#define MAX 100000//求MAX范围内的素数
long long su[MAX],cnt;
bool isprime[MAX];
void prime()
{
cnt=1;
memset(isprime,1,sizeof(isprime));//初始化认为所有数都为素数
isprime[0]=isprime[1]=0;//0和1不是素数
for(long long i=2;i<=MAX;i++)
{
if(isprime[i])
su[cnt++]=i;//保存素数i
for(long long j=1;j<cnt&&su[j]*i<MAX;j++)
{
isprime[su[j]*i]=0;//筛掉小于等于i的素数和i的积构成的合数
}
}
}
int main()
{
prime();
for(long long i=1;i<cnt;i++)
printf("%d ",su[i]);
return 0;
}
因为对于每一个数i ( i>=2)来说,如果它是非素数,那它一定会在[ 2 , i-1 ]内有一个因数,所当for语句到了i时,isprime还是1的话,说明i一定是素数。
不过这个是粗略版的代码,有很多重复的计算,比如2*3=6,在素数2的时候筛选了一遍,在素数为3时又筛选了一遍。浪费了很多时间,可能对于时间要求比较严格的题目还是可能T掉,所以需要改进一下代码。
线性筛
每次只要筛选小于等于i的第一个素因子的素数与i的乘积,既不会造成重复筛选,又不会遗漏, 时间复杂度O(N)。
为什么不会重复筛呢? 每个数都会被它的第一个素因子筛掉所以不会重复
代码如下:
int pri[N+9>>1],now;
bool vis[N+9];
void init(){
for(int i=2;i<=N;i++){
if(!vis[i])pri[++now]=i;
for(int j=1;j<=now&&pri[j]*i<=N;j++){
vis[pri[j]*i]=1;
if(i%pri[j]==0)break;
}
}
}