素数筛(根据个人理解)

素数筛的意义

素数筛就是筛的素数的一种工具

素数筛的筛法

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;
}


时间复杂度O(n^2)

有点高(难受)

我们不妨想想x-1是否能x的因数呢(x-1>1)

我就能发现x的最大的因数一定小于等于\sqrt x
 
所以我们就只要枚举(1,\sqrt x]所以可以写出以下代码。

#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;
}


时间复杂度O(n\sqrt n)

不是很高了,but还有更低的。但这就涉及到下个单元埃式筛。

2.埃式筛


还记的小学推导素数的方法吗?

把每个素数的倍数都滑掉

比如

1不是质数故滑掉

1234
5678
9101112
13141516

把的2倍数滑掉

1234
5678
9101112
13141516

把的3倍数滑掉

1234
5678
9101112
13141516


剩下就不一一列举了

根据上面的模拟我们可以写出以下代码

#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;
}


时间复杂度O(n\log \log n)(有点神奇)

线性筛


当我们观察埃式筛,就会发现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; 
}


我们可以模拟一下,去掉与与保留的区别:

去掉:

i=2,tot=1

因为f_i=1,所以a_{tot}=2

j=1

f_{a_{j}*i}=0

i=3,tot=2

因为f_i=1,所以a_{tot}=3

j=1

f_{a_{j}*i}=0

j=2

f_{a_{j}*i}=0

i=3

因为f_i=0,所以不变

j=1

f_{a_{j}*i}=0

j=2

f_{a_{j}*i}=0 (3*4=12,但12的最小质因数是2,所以会在j=1,i=6时再次历遍一次)

...

保留:

i=2,tot=1

因为f_i=1,所以a_{tot}=2

j=1

f_{a_{j}*i}=0

i=3,tot=2

因为f_i=1,所以a_{tot}=3

j=1

f_{a_{j}*i}=0

j=2

f_{a_{j}*i}=0

i=3

因为f_i=0,所以不变

j=1

f_{a_{j}*i}=0

因为i\ mod\ a_j=0所以直接退出循环

...

粗略得出时间复杂度为O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值