欧拉素数筛理解与模板

模板:

const int N = 1e8 + 5;

int cnt;
int prime[N];
bool vis[N];

void getprime(int n) {
    memset(vis, 0, sizeof vis);
    for (int i = 2; i <= n; ++i) {
        if (!vis[i]) prime[cnt++] = i;
        for (int j = 0; j < cnt && i * prime[j] <= n; ++j) {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0) break;
        }
    }
}

理解:

第一,为什么能直接用vis来判断是否为素数呢?关键在于内层循环,我们可以发现当外层循环进行到某个数k的时候,当前所算出来的最大素数一定是小于等于k的,然后内层循环在之前又干了什么呢?它每次会把素数的i倍筛掉,而我们知道进行到每个i的时候,其当前的素数数组里面的数都是小于等于i的,也就是说每次都是筛掉其素数数组内大于素数自身的某个倍数,那么她是从哪一倍开始筛的呢,那必然是素数自己本身。因为每个素数第一次被筛的倍数即最小被筛的倍数就是她刚刚被判断为素数的时候,再往后就都是筛大于她自身的倍数了,且是逐一往上加的。那我们知道,如果一个数k是合数,那么她肯定可以拆成一个其最小质因数即某一个小于k的素数和另一个大于等于其最小质因数且小于k的数相乘。那假设现在进行到某个i需要判断是否为素数,那么我们之前会把当前小于i的素数的【素数自身】至【i-1】倍全部筛掉,所以如果在当前的i还没被筛掉,就说明她没办法在小于她的素数中找到可以整除的数字,那她必然是素数了。所以,让我们外层循环从2到n走一遍,我们就能得到2到n的所有素数。

第二,为什么内层循环有个break呢,没有break正确吗?没有当然也是正确的,break出来是为了加快速度防止重复筛选的。为什么呢,想不通我们就继续来分析看看,个人认为这里举个例子来理解非常快。假设现在的i为4,那么现在我们的prime数组里面应该存的是2,3,那么现在以i为4进入内层循环,第一次往下走发现符合break条件,倘若我们没有break继续往下走,我们会去筛掉4 × 3,但我们有必要现在筛她吗?完全没有,4 × 3不就是2 × 2 × 3也就是2 × 6吗,我们在i=6的时候不就又筛了一遍吗。理解了这个来总结一下,如果素数数组里面出现了i的因数,那么就直接可以break,因为后面准备筛的数字完全可以由i的素数因数和另一个不同的i筛去,或者再讲的深一点,我们只将素数数组作为合数的最小质因数去筛数字,这样就能保证每个合数只被筛一次,因为其最小质因数只有一个,每个倍数也只被筛了一次,所以能保证每个数只被筛一次,复杂度为O(n)。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值