素数筛的意义
素数筛就是筛的素数的一种工具
素数筛的筛法
1.普通的筛
素数有一特点,就是除了1与它本身就没有其他因数了。
不妨想想枚举因数。
所以可以写出以下代码。
#include<iostream>
using namespace std;
int zsl[1003],tot=0;
bool zs(int x){//x是判断的素数
for(int i=2;i<x;i++)//枚举2~x-1(因数枚举)
{
if(x%i==0)//有另外的因数
{
return 0;//返回不是
}
}
return 1;//返回是
}//判断素数的函数
int main(){
int n=1003;
for(int i=2;i<=n;i++)
{
if(zs(i))//如果是素数
{
zsl[++tot]=i;//加入素数数组
}
}
return 0;
}
时间复杂度
有点高(难受)
我们不妨想想是否能的因数呢
我就能发现的最大的因数一定小于等于
所以我们就只要枚举所以可以写出以下代码。
#include<iostream>
#include<cmath>
using namespace std;
int zsl[1003],tot=0;
bool zs(int x){//x是判断的素数
for(int i=2;i<sqrt(x);i++)//枚举2~根号(x)(因数枚举)
{
if(x%i==0)//有另外的因数
{
return 0;//返回不是
}
}
return 1;//返回是
}//判断素数的函数
int main(){
int n=1003;
for(int i=2;i<=n;i++)
{
if(zs(i))//如果是素数
{
zsl[++tot]=i;//加入素数数组
}
}
return 0;
}
时间复杂度
不是很高了,but还有更低的。但这就涉及到下个单元埃式筛。
2.埃式筛
还记的小学推导素数的方法吗?
把每个素数的倍数都滑掉
比如
1不是质数故滑掉
2 | 3 | 4 | |
5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 |
把的2倍数滑掉
2 | 3 | ||
5 | 7 | ||
9 | 11 | ||
13 | 15 |
把的3倍数滑掉
2 | 3 | ||
5 | 7 | ||
11 | |||
13 |
剩下就不一一列举了
根据上面的模拟我们可以写出以下代码
#include<iostream>
#include<cstring>
using namespace std;
int zsl[1003],tot=0;
bool zsf[1003];
int main(){
int n=1003;
memset(zsf,1,sizeof(zsf));//数组变成都是素数
zsf[1]=0;//1不是
for(int i=2;i<=n;i++)//枚举
{
if(zsf[i])//是否是素数
{
zsl[++tot]=i;//是就加进素数数组
for(int j=2;j*i<=n;j++)//枚举倍数(不能从1开始,因为素数有1这个因数)
{
zsf[i*j]=0;//倍数都不是素数
}
}
}
return 0;
}
时间复杂度(有点神奇)
线性筛
当我们观察埃式筛,就会发现6会被2和3各操作一次(有点浪费)
不妨想想
合数=最小质因数*因数
那我们可以枚举最小质因数,但要注意必须是最小质因数(不然会重复)我就可以写出以下代码
#include<iostream>
#include<cstring>
using namespace std;
const int N=1e4+3;
int a[N],tot=0;
bool f[N];
int main(){
ios::sync_with_stdio(0);//cin,cout加速(没必要)
memset(f,0,sizeof(f));
f[0]=0,f[1]=0;
for(int i=2;i<=N;i++)//枚举n
{
if(f[i])//如果是素数
{
a[++tot]=i;//加入素数数组
}
for(int j=1;j<=tot&&a[j]*i<=n;j++)//枚举素数
{
f[a[j]*i]=0;//不是素数
if(i%a[j])//如果这个数是这个素数的倍数,那下一个数一定不是最小质因数。
{
break;//退出循环
}
}
}
}
关于
if(i%a[j]==0)
{
break;
}
我们可以模拟一下,去掉与与保留的区别:
去掉:
因为,所以
因为,所以
因为,所以不变
(3*4=12,但12的最小质因数是2,所以会在时再次历遍一次)
...
保留:
因为,所以
因为所以
因为,所以不变
因为所以直接退出循环
...
粗略得出时间复杂度为