1e6 78498
1e7 664579
1e8 5761455
埃氏筛(慢)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e8;
int prime[MAXN+1],book[MAXN+1],cnt;
void getPrime()
{
cnt=0;
memset(book,0,sizeof(book));
book[0]=book[1]=1;
int i,j;
for(i=2;i<=MAXN;i++)
{
if(!book[i])
{
prime[++cnt]=i;
if(i>MAXN/i) continue;
for(j=i*i;j<=MAXN;j+=i)
book[j]=1;
}
}
}
int main()
{
getPrime();
printf("%d\n",cnt);
return 0;
}
为什么从 j = i * i 开始,不是 j = i * 2
模拟出 j = i * 2 的表,会发现 i * (i-1) 之前标记过。
该算法无法避免的重复标记
比如:标记 3 * 49 和标记 7 * 21。
线性筛–欧拉筛(快)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e8;
int prime[MAXN+1],book[MAXN+1],cnt;
void getPrime()
{
cnt=0;
memset(book,0,sizeof(book));
book[0]=book[1]=1;
int i,j;
for(i=2;i<=MAXN;i++)
{
if(!book[i])
prime[++cnt]=i;
for(j=1;j<=cnt&&i<=MAXN/prime[j];j++)
{
book[i*prime[j]]=1;
if(i%prime[j]==0) break;
}
}
}
int main()
{
getPrime();
printf("%d\n",cnt);
return 0;
}
理解该算法的关键:if(i%prime[j]==0) break;
换句话理解:为什么不标记 i * prime[j+1] ?
拆分该表达式就可明白:
[ prime[j+1] * (i / prime[j]) ] * prime[j]
i * prime[j+1]
[ prime[j+1] * (i / prime[j]) ] > i ,同时 prime[j] < prime[j+1]
根据代码,现在不标记,后面一定会标记的。
埃氏筛法升级版(快)
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e8;
int prime[MAXN+1],book[MAXN+1],cnt;
void getPrime()
{
cnt=0;
memset(book,0,sizeof(book));
book[0]=book[1]=1;
int i,j;
for(i=2;i<=(int)sqrt(MAXN);i++)//遍历sqrt(n)以内的素数
{
if(!book[i])
{
if(i>MAXN/i) continue;
for(j=i*i;j<=MAXN;j+=i)//标记合数
book[j]=1;
}
}
for(i=2;i<=MAXN;i++)
if(!book[i]) prime[++cnt]=i;
}
int main()
{
getPrime();
printf("%d\n",cnt);
return 0;
}
稍微解释一下原理:
合数可以由素数相乘而得
判断是是否为素数,只需判断sqrt(n)以内的因子即可.
因此只需判断sqrt(n)以内的素数即可.
注意:
——1e9以内的素数个数用map容器标记跑不出来结果,放弃。