欧拉、埃氏、区间素数筛

1.欧拉素数筛。
复杂度在O(N)。比埃氏素数筛要快。
埃氏素数筛对于某些数会重复筛选,比如12,会被2,4,6都筛选一遍,而欧拉素数筛解决了重复筛选的问题,它对于12只筛选一遍。
思路:如果一个数已经有了一个质因数(不是它本身),就不再筛选(见代码)

  #include<bits/stdc++.h>
#define maxn 1000010
#define ll long long
using namespace std;
int prime[maxn];//记录素数
int fac[maxn];//记录i的最小质因数
int top;
int judge(int n)
{
    int i,j;
    top=0;
    memset(fac,0,sizeof(fac));//全都初始化为没有质因数
    for(i=2;i<=n;i++)
    {
        if(!fac[i])//从2开始,如果没有质因数,i是素数
        {
            prime[++top]=i;
            fac[i]=i;//素数的最小质因数是它本身
        }
        for(j=1;j<=top&&prime[j]*i<=n;j++)
        {
            fac[prime[j]*i]=prime[j];//记录每个素数prime[j]的i倍的最小质因数
            if(i%prime[j]==0)//节约时间的关键。如果i已经有了质因数,退出循环,避免重复筛选(比如i=4时,会把8标记出来,但是4已经有了最小质因数,所以它会跳出循环,不再标记12,而12会在6时被标记。同样18会在9时标记,而不是6时)
               break;
        }
    }
    return top;
}
int main()
{
    int n;
    cin>>n;
    int ans=judge(n);
    cout<<ans<<endl;
   /* for(int i=1;i<=ans;i++)
    {
        cout<<prime[i]<<' ';
    }
    cout<<endl;*/
    return 0;
}

2.埃氏素数筛(适用于1e6左右的数据,再大就明显慢了)
给你一个数n,让你求从1到n的所有素数。
如果n的范围很大,那一个数一个数判断是不是素数一定会超时。
素数筛:做一个从2到n的表,先全都标记成是素数。表里最小的数是2, 2是素数,把2的倍数全都划去(都不是素数),下一个最小的数是3,3是素数,把3的倍数也全都划去…表里未被处理的最小的数是m,
则m是素数(m没有被划去,说明m没有除了1和m本身外的因数),把m的倍数全都划去,这样剩下的数都是素数,这个表就是1到n的素数表。

  //在1e6左右速度可以,到1e7就比较慢了
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e7+5;
bool isprime[N];
int prime[N];
int top;
int judge(int n)
{
    memset(isprime,true,sizeof(isprime));
    isprime[1]=isprime[0]=false;
    top=0;
    for(int i=2;i<=n;i++)
    {
        if(isprime[i])
        {
            prime[++top]=i;
            for(int j=2*i;j<=n;j+=i)
            {
                isprime[j]=false;
            }
        }
    }
    return top;
}
int main()
{
    int n,ans;
    cin>>n;
    ans=judge(n);
    cout<<ans;
    return 0;
}

3.区间素数筛(大数适用素数筛,a<b<10^12, b-a<1e6)(注意a,b的区间距离在1e6左右)
一个合数b的质因子最大不超过sqrt(b),例如9(3),14(2), 15(3), 16(2)…
所以我们可以先打一个(2,sqrt(b))的素数表,这样sqrt(b)的范围最大就1e6了。在这个素数表里再删除素数的倍数。

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
typedef long long ll;
ll prime[N];
ll top;
bool is_primesmall[N];
bool is_prime[N];
ll judge(ll a,ll b)//sqrt函数参数和返回值都为double型,精度不确定,尽量少用
{
    memset(is_primesmall,true,sizeof(is_primesmall));
    memset(is_prime,true,sizeof(is_prime));
    for(ll i=2;i*i<b;i++)
    {
        if(is_primesmall[i])//用小区间 [ 2,sqrt(b) )去打表
        {
            for(ll j=2*i;j*j<b;j+=i)  is_primesmall[j]=false;//筛选[2,sqrt(b));
            for(ll j=max(2LL,(a+i-1)/i)*i;j<b;j+=i)  is_prime[j-a]=false;//大数a,b下标偏移。(a+i-1)/i表示>=a的最小的数。j至少得是2*i, 因为i是素数,如果j=1*i,is_prime[i]标记成i不是素数
        }
    }
    for(ll i=(a==1);i<b-a;i++)//区间坐标统一向左偏移了a。1不是素数,但是is_prime[0]=true,所以如果a=1,要排除这种情况
    {
        if(is_prime[i])  prime[++top]=i+a;
    }
    return top;
}
int main()
{
    ll a,b;
    ll ans;
    cin>>a>>b;
    ans=judge(a,b);
    cout<<ans<<endl;
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值