筛质数

139 阅读2分钟

准备从头开始进行算法中数学知识的学习。

题目描述

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

输入格式

共一行,包含整数 nn

输出格式

共一行,包含一个整数,表示 1n1∼n 中质数的个数。

数据范围

1n1061≤n≤10^6

输入样例:

8

输出样例:

4

题目分析

这是一道数论的经典题目,主要知识点是关于质数的应用。

根据题目所给的数据范围的大小,我们将进行不同算法的选择。

O(n*sqrt(n))

这个方法就是最基础的枚举法,对于从 1n1\sim n 中的所有数,我们统一用一个方法进行质数的判断。即对于一个数 xx,我们从 22 开始枚举到 x\lfloor\sqrt{x}\rfloor,若这个范围区间的数存在被 xx 整除的数,则证明 xx 不是质数,反之则为质数。

O(nloglogn)

接下来介绍埃氏筛法。

首先我们知道一个数 xx 只能被小于 xx 的数整除,而且在所有能整除 xx 的数中,最小的那个是一个质数,其余的数同样被那个最小的质数整除。在此前提下给定求解的范围 1n1\sim n,我们从 22 开始枚举到 nn,每次枚举当前数的倍数并递增枚举,并对除第一次枚举的数做标记,意为其可以被第一个数整除,即不是质数;在进行下一次枚举时对不是质数的数直接跳过即可。

欧拉筛法(线性筛)

线性筛法是埃氏筛法的一个优化,其是针对埃氏筛法会多次筛去合数的一个改变。

其核心思想为 nn 一定会且只会被其最小质因子筛掉,具体表现在有两个方面:

  1. 一个数一定会被其最小质因子筛去。对于数 xx,设其最小质因子为 pxpx,在枚举到 xx 之前一定会枚举到 x/pxx / px,即其一定会被筛去。
  2. 一个数只会被其最小质因子筛去。若当前枚举到 ii,质数枚举到 p[x]p[x],若 i%p[x]==0i \% p[x] == 0,意味着 p[x]p[x]ii 的最小质因子,则直接跳出。

Accept代码

#include <iostream>

using namespace std;

const int N = 1e6 + 10;

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

int main()
{
    cin >> n;

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

    cout << cnt;
    return 0;
}