素数筛简介

素数筛

素数筛的基本思想:

  1. 标记一个范围内的数字是否为合数,没有标记的为素数

  2. 算法的空间复杂度为O(N),时间复杂度为O(N * loglogN)

  3. 总体的思想是用素数去标记掉不是素数的数字,例如我知道了 i 是素数,那么2 * i、3 * i、4 * i…就都不是素数

一、原始筛法

暴力枚举,代码简单,但时间复杂度高,运算较大的数字有明显卡顿感。

思路: 列举2 ~ sqrt(n)的所有数字,用n去除以,若都不能被整除,n就是质数

int is_prime1(int n){
	for(int i=2,I=sqrt(n);i<I;i++){
        if(n%i==0)return 0;
    }
    return 1;
}

二、埃氏筛法

思路: 0表示是素数,1表示不是素数。0,1均不是素数,从2开始,把2的倍数标记为1,一直到大于n,然后从下一个素数3开始,进行同样的操作。

#define MAX_N 100
int not_prime[MAX_N] = {1,1,0};
int prime[MAX_N] = {0};
void Erat_prime(int n){
    for(int i=2;i<=n;n++){
        if(!not_prime[i])prime[++prime[0]]=i;
        for(int j=2;j*i<=n;j++){
            not_prime[i*j]=1;
        }
    }
}

//对于内层循环代码,先标记首个数为素数,再将它的倍数标记为合数

for(int j=2*i;j<=MAX_N;j+=i){
    not_prime[j]=1;
}

因为这种标记重复太多,比如2把6标记一次,3又把6标记一次。对此,我们可以优化一下,进行向上标记,优先标记自己的倍数,如下:

for(int j = i*i;j<=MAX_N;j+=i){
    not_prime[j]=1;
}

但是这样标记会出现一个问题,比如 i 循环到 MAX_N ,j = MAX_N * MAX_N,这样就会出现爆栈的情况,于是,我们再做以下优化

for(int j=j;j*i<=n;j++){
    not_prime[i*j]=1;
}

三、欧拉筛

思路: 在用埃氏筛的时候,同一个数字也许会被筛选多次,欧拉筛就是在埃氏筛的基础上,让每个合数只被它的最小质因子筛一次,以达到不重复的目的

用一条语句if(i%prime[j]==0)break;,使每个合数只被它的最小质因子筛选一次,从而避免重复筛选

#define MAX_N 100

int prime[MAX_N+5] = {0};
int is_prime[MAX_N+5] = {0};
void init_prime(){
    for(int i=2;i<=MAX_N;i++){
        if(!is_prime[i])prime[++prime[0]]=i;
        for(int j=0;j<prime[0];j++){
            if(prime[j]*i>MAX_N)break;
            is_prime[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值