素数的筛选方法:从暴力筛选到线性筛选

暴力筛选

也被就叫做试除法,对于每一个整数N,你都可以进行一次从2到根号N的筛选,如果这个数不能被从2到根号N的任意一个数整除,就可以确定这个数是素数.

int prime[maxn],num;
void Simple_Screening(int n)
{
    for(int i=2;i<=n;i++){
        int flag=0;
        for(int j=2;j*j<=i;j++){
            if(!(i%j)) {flag=1;break;}
        }
        if(!flag) prime[num++]=i;
    }
}

对于这种最朴素的方法所需的时间复杂度也是最高的,对于每一个N你需要O(n2)的时间复杂度。

埃氏筛(Eratosthenes)

朴素的筛选方法在竞赛中肯定是行不通的。那么我们这样考虑一个问题:对于每个质数,他的任意不包括一的整数倍必然是一个合数。我们可以对从2到N的整数这样筛选:每当找到一个质数时,便帮他的任意倍标记为合数。如果这个数被标记为合数,那便跳过对这个数的操作。

int prime[maxn],num,visit[maxn];
void Eratosthenes(int n)
{
    for(int i=2;i<=n;i++){
        if(!visit[i]){
            for(int j=2;j*i<=n;j++){
                visit[i*j]=1;
            }
            prime[num++]=i;
        }
    }
}

那么,这种方法的时间复杂度是在2到N中所有的质数的N/P的加和。化简后也就是O(N log log N),这个复杂度接近线性,比赛时大多数时候使用这种方法。

线性筛(欧拉筛)

埃氏筛是非常接近线性的筛法,但是当遇到n=10000000的情况时还是会被卡掉。我们对埃氏筛分析会发现,这个数i有多少个质因子就会被筛掉多少次,对此我们可以这样处理

int prime[maxn],num,visit[maxn];
void Euler(int n)
{
    for(int i=2;i<=n;i++){
        if(!visit[i]) prime[num++]=i;
        for(int j=0;j<num,prime[j]*i<=n;j++){
            visit[i*prime[j]]=1;
            if(!(i%prime[j])) break;
        }
    }
}

注意这里面的这个if语句

if(!(i%prime[j])) break;

这个语句就表明的每个合数只被他的最小质因子标记一次。
没有这个语句,会继续标记i * prime[j+1],但是i里有个质因子是prime[j],也就意味这这个被标记的数同时也会被prime[j] * (i/prime[j]*prime[j+1])标记一次。这样就会发成同一个合数被标记多次的情况。也就是说这个if语句使得同一个合数被标记且只被标记一次。当然这个算法的时间复杂度是O(n)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值