线性筛与埃氏筛法

埃氏筛法(埃拉托斯特尼筛法)

在求解小于n有多少个素数的问题中,当n的数值较大时,暴力求解显然是不可取的。
埃氏筛法的原理:任意大于1的正整数n,它的x倍(x>1)就是合数,由此来筛选出素数。
可以这样理解,暴力求解是直接通过遍历计算判断这个数是否是素数;而埃氏筛法则是创建一个大小为N的数组,通过倍数关系筛选出素数。

vector<int> Eratosthenes(int N)
{
    vector<int> Pri;                                //存储筛选出来的素数
    vector<bool> is_Pri(N, true);                   //标记素数,初始均为true,通过倍数关系排除掉合数
    for(int i = 2; i < N; i++)
    {
        if(is_Pri[i])                               
            Pri.push_back(i);
        for(int j = i * i; j < N; j += i)           
            is_Pri[j] = false;
    }
    return Pri;
}

为什么从i*i开始筛选掉合数:被省略的部分 {i * 2, i * 3, ..., i * (i - 1)},不难发现在排除(i-1)的倍数时就已经进行过(i-1) * i的运算(例如i*2, 在排除2的倍数时,就已排除了2*i),故可省略掉。
但在埃氏筛法中,存在着同一个数被反复标记的情况(例如18,在2*9中被标记,在3*6中再次被标记),故线性筛诞生了。

线性筛(Euler筛法)埃氏筛法的升级版

本文对于线性筛的理解:一个数若为合数,则其一定等于两个小于它的数的乘积(除1和其本身以外),在此取其最小质因数与另一个值的乘积,所以我们可以通过将所有质数依次与(2, 3, 4, …, N-1)相乘来标记合数,但依旧存在很多重复标记的地方(例如2*93*6)。
解决方法:在进行运算 i * Pri[m](Pri[m]存储素数)时,如果iPri[m]的倍数,即i = k * Pri[m],故计算i * Pri[m+1]时,可变换为i * Pri[m+1] = k * Pri[m] * Pri[m+1]在后续的遍历计算中会再次出现i = k * Pri[m+1]情况,故此时i不用提前与Pri[m+1]进行乘积,从而达到减少重复筛选数量。

此时依旧存在优化空间,可参考埃氏筛法中从i*i开始筛选。素数Pri[m]只与大于它的i相乘,若i小于Pri[m],在后续计算中均会被计算,若i为素数,在后续计算中存在I = Pri[m]Pri[n] = i进行Pri[n] * I乘积,若i为合数,可将其拆分为i = Pri[n] * k,与上述线性筛同理可进行省略,避免提前运算。

vector<int> Euler(int N)
{
    vector<int> Pri;
    vector<bool> not_Pri(N);
    for(int i = 2; i < N; i++)
    {
        if(!not_Pri[i])
            Pri.push_back(i);
        for(int tmp_Pri : Pri)
        {
            if(tmp_Pri * i > N)
                break;
            not_Pri[tmp_Pri * i] = true;
            if(i % tmp_Pri == 0)
                break;
        }
    }

    return Pri;
}

参考文章:筛法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值