数论 - 质数筛法(埃式筛 + 线性筛)

数论 - 质数筛法(埃式筛 + 线性筛)


给定一个正整数n,请你求出1~n中质数的个数。

输入格式
共一行,包含整数n。

输出格式
共一行,包含一个整数,表示1~n中质数的个数。

数据范围
1≤n≤106

输入样例:
8
输出样例:
4

1、埃式筛

做法:

对 每 一 个 质 数 , 筛 去 该 质 数 的 所 有 倍 数 即 可 。 对每一个质数,筛去该质数的所有倍数即可。

i 从 2 开 始 依 次 遍 历 , 只 要 i 未 被 标 记 过 , 说 明 i 一 定 是 质 数 。 因 为 对 每 一 个 质 数 , 我 们 都 会 先 筛 去 其 倍 数 。 假 设 i 是 合 数 , 那 么 i 必 有 质 因 子 p i ( p i < i ) , i 必 然 已 经 被 筛 去 。 i从2开始依次遍历,只要i未被标记过,说明i一定是质数。因为对每一个质数,我们都会先筛去其倍数。\\假设i是合数,那么i必有质因子p_i(p_i<i),i必然已经被筛去。 i2iiiipi(pi<i)i

时 间 复 杂 度 : O ( n l o g ( l o g n ) ) 。 时间复杂度:O(nlog(logn))。 O(nlog(logn))

代码:

#include<iostream>

using namespace std;

const int N = 1e6+10;

int n,cnt,prime[N];
bool st[N];

void get_prime()
{
    for(int i=2;i<=n;i++)
        if(!st[i])
        {
            prime[cnt++]=i;
            for(int j=i;j<=n;j+=i) st[j]=true;
        }
}

int main()
{
    cin>>n;
    
    get_prime();
    
    cout<<cnt<<endl;
    
    return 0;
}

2、线性筛

核 心 思 路 : 每 个 合 数 均 会 被 其 最 小 质 因 子 筛 去 。 核心思路:每个合数均会被其最小质因子筛去。

① 、 i 从 2 开 始 依 次 枚 举 , 只 要 i 未 被 筛 过 , 说 明 i 是 质 数 , 将 其 存 储 下 来 。 ①、i从2开始依次枚举, 只要i未被筛过,说明i是质数,将其存储下来。 i2ii

② 、 从 前 到 后 依 次 枚 举 每 个 质 数 p j , 把 每 个 p j 的 i 倍 筛 去 , 直 到 枚 举 到 i 的 最 小 质 因 子 或 者 p j × i > n 。 ②、从前到后依次枚举每个质数p_j,把每个p_j的i倍筛去,直到枚举到i的最小质因子或者p_j×i>n。 pjpjiipj×i>n

下 面 解 释 如 何 保 证 所 有 合 数 均 被 筛 去 : 下面解释如何保证所有合数均被筛去:

对 于 任 意 合 数 , 均 对 应 一 个 最 小 质 因 子 p j 。 i 是 从 小 到 大 枚 举 的 , 那 么 所 有 最 小 质 因 子 是 p j 的 合 数 均 会 被 筛 去 。 对于任意合数,均对应一个最小质因子p_j。\\i是从小到大枚举的,那么所有最小质因子是p_j的合数均会被筛去。 pjipj

① 、 若 i 是 质 数 , 将 依 次 筛 去 每 一 个 p j × i , 显 然 p j × i 的 最 小 质 因 子 也 是 p j , 因 为 循 环 顺 序 保 证 了 i > = p j 。 ①、若i是质数,将依次筛去每一个p_j×i,显然p_j×i的最小质因子也是p_j,因为循环顺序保证了i>=p_j。 ipj×ipj×ipji>=pj

② 、 若 i 是 合 数 , 那 么 必 然 存 在 p j 是 i 的 最 小 质 因 子 , 就 已 经 在 i 枚 举 到 p j i 时 被 筛 去 了 。 ②、若i是合数,那么必然存在p_j是i的最小质因子,就已经在i枚举到\frac{p_j}{i}时被筛去了。 ipjiiipj

时 间 复 杂 度 O ( n ) 。 时间复杂度O(n)。 O(n)

代码:

#include<iostream>

using namespace std;

const int N = 1e6+10;

int n,cnt,prime[N];
bool st[N];

void get_prime()
{
    for(int i=2;i<=n;i++)
    {
        if(!st[i]) prime[cnt++]=i;
        for(int j=0;prime[j]<=n/i;j++)
        {
            st[i*prime[j]]=true;
            if(i%prime[j]==0) break;
        }
    }
}

int main()
{
    cin>>n;
    
    get_prime();
    
    cout<<cnt<<endl;
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值