朴素算法
优化方法
1.只判断奇数
2.因子是成对出现,并且分布在 sqrt (n) 两侧的 所以只判断到n的根

[素数筛] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
int IsPrime(int n)
{
int i;
if(n<2||(n!=2&&n%2==0))
return 0;
else //n都是奇数
{
for(i=3;i*i<=n;i+=2)
{
if(n%i==0)
return 0;
}
return 1;
}
}

显然如此优化依旧不够

埃氏筛1.0
便是数组a储存已经筛选过的素数 数组b包含范围内所有数字,用来标记是否被访问过 从2开始循环 对其进行倍增操作
优化方法
1.易知很多倍增其实重复了 浪费了时间空间 优化方法便是只对素数进行倍增
2.倍增从i开始 而不是从2开始 减少一半计算量

[素数筛] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

int vis[N];//判断是否被访问过
int prime[N];//存储筛选出来的素数

int s(int n)
{
int i,j,k;//i是逐个访问 j二轮标记,k标记prime数组的位置
k=0; //这样定义 每次函数调用时都清空
memset(vis,0,sizeof(int)*N); //清空访问数组
vis[0]=vis[1]=1;//此步无所谓 因为从2开始
for(int i=2;i<=n;i++)
{
if(vis[i]==0)
{
prime[k++]=i;//如果没有被访问过 那么就是最小的素数
for(int j=i;j<=n;j++) vis[i*j]=1; //标记在上界内 这个素数数倍增后的数
}
}
return k;
}

埃氏筛2.0 除了上述优化
1.只判断奇数
2.由于因子是成对出现的,如果左侧范围数字不是因子,则右侧范围中也没有因子,因此可以说明这个数字是素数
即x在[2,sqrt (x) ] 范围内的数倍增时 已经得到了
于是有了最终埃氏筛

[埃氏筛] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int E(int n)
{
if(n<2) return 0;
int i,j,k;
k=0;
memset(vis,0,sizeof(int)*N);
vis[0]=vis[1]=1;//0 1都是非素数

for(i=4;i<=n;i+=2) vis[i]=1;//把偶数全部标记了 都是非素数
for(i=3;i*i<=n;i+=2)
{
if(vis[i]==0)
{
for(j=i;i*j<=n;j++) vis[i*j]=1;//这里把j=2改为j=i
}
}
prime[k++]=2; //上面循环 只循环到了sqrt(n) 所以需要从头循环一次
for(i=3;i<=n;i+=2)
{
if(vis[i]==0) prime[k++]=i;
}
return k;
}


埃氏筛的最终版本 是只要是素数就进行倍增
欧拉筛 在埃氏筛的基础上 保证了每个合数只被筛选一次

[欧拉筛] [c++]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//欧拉筛函数
int E2(int n)
{
int i,j,k;
k=0;//保存素数的个数
memset(vis,0,sizeof(int)*maxn);//初始化数组
for(i=2;i<=n;i++)
{
if(vis[i]==0)//i是素数,则存起来
prime[k++]=i;
for(j=0;j<k;j++)//进行倍增,用i去乘以i之前(包括i)的素数
{
if(i*prime[j]>n)//倍增结果超出范围,退出
break;

vis[ i*prime[j] ]=1;//将倍增结果进行标记

if(i%prime[j]==0)//i是前面某个素数的倍数时,也需要退出
break;
}
}
return k;
}