AcWing874-线性筛选质数

题解

       这一题看起来很简单,不就是质数吗?但是要在$O(n)$的时间复杂度内实现,还是比较难理解的,在此之前会介绍朴素筛法和埃氏筛法,最后介绍线性筛法。

一、朴素筛法

       朴素筛法相对比较简单,在 i 枚举 1 - n 的过程中,不断筛掉 i 对应的倍数即可,这样的确可以把所有的合数都筛掉,但是会有大量重复筛的过程,例如 2 会筛 12,3 会筛 12,6 也会筛12,时间复杂度估计为$O(nlog(n))$。

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 1000010;
int n;
bool st[MAXN];

void primes(int n)
{
    int cnt = 0;
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) {
            cnt++;
        }
        // 筛 i 对应的倍数 
        for (int j = i; j <= n; j += i) {
            st[j] = true;
        }
    }
    cout << cnt << endl;
}

int main()
{
    cin >> n;
    memset(st, false, sizeof(st));
    primes(n);
    return 0;
}

 

二、埃氏筛法

       埃氏筛法是在朴素筛法的基础上进行了优化,即只筛质数对应的倍数即可,因为所有的合数都可以拆成质数的乘积,当然,这类筛法还是存在重复筛的过程,例如 2 会筛 6,3 也会筛 6,时间复杂度估计为$O(nloglog(n))$。

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 1000010;
int n;
bool st[MAXN];

void primes(int n)
{
    int cnt = 0;
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) {
            cnt++;
            // 只筛质数,因为其它数都已经被筛过 
            for (int j = i; j <= n; j += i) {
                st[j] = true;
            }
        }
        // 筛 i 对应的倍数 
    }
    cout << cnt << endl;
}

int main()
{
    cin >> n;
    memset(st, false, sizeof(st));
    primes(n);
    return 0;
}

 

三、线性筛法

       线性筛法可以保证每个数都只会被筛一次,时间复杂度为$O(n)$,那为何呢?

       在筛的过程中,我们是用最小质因子去筛选,在此每个合数都只会被最小质因子筛掉且被筛一次,分两种情况进行讨论。

       1、i % pj == 0,pj 为 i 的最小质因子,pj 也为 i * pj 的最小质因子。

       2、i % pj != 0,pj 小于 i 的最小质因子,但 pj 是为 i * pj 的最小质因子。

       此时的 i * pj 就可以被筛掉,且每一个合数有且只有一个最小质因子,因此,每一个合数都只会被其最小质因子筛掉且被筛一次。

#include <iostream>
#include <cstring>
using namespace std;

const int MAXN = 1000010;
int n;
int prime[MAXN];
bool st[MAXN];

// 线性筛法 
void primes(int n)
{
    int cnt = 0;
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) {
            prime[cnt++] = i;         
        }
        // 从小到大枚举质数
        // 每个合数都只会被其最小质因子筛一次 
        for (int j = 0; prime[j] <= n / i; ++j) {
            st[prime[j] * i] = true;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
    cout << cnt << endl;
}

int main()
{
    cin >> n;
    memset(st, false, sizeof(st));
    primes(n);
    return 0;
}

       好啦,最后一种筛法很重要哦!

posted @ 2020-10-30 00:12  Fool_one  阅读(125)  评论(0编辑  收藏  举报