素数筛选(埃氏筛法,区间筛法,欧拉筛法)

一、埃氏筛法

首先将2到n范围内的所有整数写下来。其中最小的数字2是素数。将表中所有2的倍数都划去。表中剩余的最小数字是3,他不能被更小的数整除,所以3是素数。再将表中所有3的倍数都划去。如果表中剩余的最小数字是m时,m就是素数。然后将m的倍数都划去。依次类推,反复操作,就能依次枚举出n以内的素数
在这里插入图片描述

代码如下:

int prime[MAXN];//第i个素数
bool is_prime[MAXN+1];//is_prime[i]为true时表示i为素数

void init(){
    int p=0;
    for(int i=0;i<n;i++){
        is_prime[i]=true;
    }
    is_prime[0]=is_prime[1]=false;
    for(int i=2;i<=n;i++){
        if(is_prime[i]){
            prime[p++]=i;
            for(int j=2*i;j<=n;j+=i){
                is_prime[j]=false;
            }
        }
    }
}

二、区间筛法

如:在区间[a,b)内有多少个素数
素数判定中,如果d是n的约数,那么n/d也是n的约数。由n=d * n / d可知min(d,n/d)<= n \sqrt{n} n ,所以只需要检查2~ n \sqrt{n} n 就行了,故而有b以内的合数的最小质因数一定不超过 b \sqrt{b} b
如果有 b \sqrt{b} b 内的素数表的话,就可以把埃氏筛法应用到[a,b)上。也就是说,先分别做好[2, b \sqrt{b} b )的表和[a,b)的表,然后从[2, b \sqrt{b} b )的表中筛得素数的同时,也将倍数从[a,b)的表划去,剩下的就是[a,b)内的素数了

代码如下:

typedef long long ll;

bool is_prime[MAXN];//is_prime[i-a]=true <=> i是素数
bool is_prime_small[MAXN];

//对区间[a,b)内的整数筛选素数。
void segment_sieve(ll a,ll b){
    for(int i=0;(ll)i*i<b;i++)	is_prime_small[i]=true;
    for(int i=0;i<b-a;i++)	is_prime[i]=true;
    
    for(int i=2;(ll)i*i<b;i++){
        if(is_prime_small[i]){
            //筛[2,根号b)
            for(int j=2*i;(ll)j*j<b;j+=i)	is_prime_small[j]=false;
            //筛[a,b)
            for(int j=max(2LL,((a+i-1)/i)*i;j<b;j+=i)	is_prime[j-a]=false;
        }
    }
}

三、欧拉筛法(线性筛法)

欧拉筛,也叫线性筛,可以在 O(n)时间内完成对2~n的筛选。它的核心思想是:让每一个合数被其最小质因数筛到

感觉欧拉筛法和埃氏筛法的原理类似…但是欧拉筛法更减少了没有必要的计算,就是增加了处理:每一个被筛掉的数都必须是被它的最小质因子筛掉,为了保证这一点,增添了如下代码:

if(i % prime2[k] == 0)//确保是最小质因数
{
    break;
}

完整的算法代码如下(C++11风格):

bool isnp[MAXN];
vector<int> primes; // 质数表
void init(int n){
    for (int i = 2; i <= n; i++){
        if (!isnp[i])
            primes.push_back(i);
        for (int p : primes){
            if (p * i > n)
                break;
            isnp[p * i] = 1;
            if (i % p == 0)
                break;
        }
    }
}

也可以:

const int N = 1000010;
int primes[N], cnt=0;  // primes[0]~primes[cnt-1]存储的是0~n中所有的质数(从小到大)
bool st[N];  // st[i] == true说明i不是质数
// 线性选法,时间复杂度:O(n)
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] <= n / i; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

叶辰 .

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值