筛质数

c++

筛质数

/*
 * 素数筛
 * 素数、质数定义:
 *      质数,又叫素数,是指一个大于1的自然数,且除了1和它本身外,不能被其他自然数整除的数。换句话说,就是该数除了1 和它本身以外,不再有其他的因数。
 *
 * 在先前的博客中,我们介绍了试除法 O(sqrt(N)) 判断一个数字是否为素数;以及 O(sqrt(N)) 将数分解为素数,下面我们介绍素数筛。
 *
 * 素数筛判断 1..n 中的数字,分别是否为素数。
 * 1. 使用试除法作素数筛
 *      我们已知试除法判断一个数字是否为素数的复杂度是 O(sqrt(N)),那么判断 n 个数字,复杂度约为 O(N sqrt(N)) = O(N ^ 1.5)。
 *      这是因为试除法 判断第 i 个数字是否为素数,没有充分利用到前 i 个数字的信息
 *
 *      因此可以充分使用了前面素数信息的方法,思想是判断 x 是否为素数,查看 x 之前,并且小于 sqrt(x) 的素数,x是否可以整除。
 *
 *      π(x)符号,就是表示小于或等于自然数x的所有素数个数,当x趋于无穷大时,π(x)和x /ln(x)这两个数的比值趋于1。
 *      因此复杂度为 O(N log(sqrt(N))
 * 2. 埃氏筛
 *      使用改进后的试除法作为素数筛是可以,但是 x 老是会整一些除不尽的数,浪费时间,因此我们可以使用质数,否定他的倍数。
 *      计算量:
 *          n / 1 + n / 2 + n / 3 + ... + n / n = n logn
 *      但是加上素数优化,感觉会好很多。
 *
 *  3. 欧拉筛(线性筛)
 *      欧拉筛和埃氏筛都是一样,利用 st 数组,结合公倍数进行筛选,晒出合数,剩下的就是质数了,他们复杂度的优劣性在于合数被筛选了多少次?
 *      埃氏筛的算法流程可以看出,对于某个合数,他被筛出的次数为他质因数分解后,质数的个数,如 45 = 3 ^ 2 * 5,他有两个种类的质数,3和5。
 *      欧拉筛的出发点在于,能否每个合数只需要筛选一次?
 *      -> 我们使用  最小的质因数  筛选合数,那么这个合数只会被筛选一次。
 *      复杂度从名称也可以听出来,为 O(N)
 *
 *  4. 实际运行时间分析
 *      发现试除法还是耗时长,这是因为试除法所有数字都要试除,不如直接利用 st 方法筛选出他们的倍数方便。
 */
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdio>

using namespace std;

const int N = 1000010;
int n;
vector<int> prime;
bool st[N];


// 优化之后的试除法判质数
// @test-time: 231 ms
void sample_seive(int n) {
    prime.clear();
    memset(st, false, sizeof st);

    if (n <= 1) {
        return;
    }

    st[1] = true;
    for (int i = 2; i <= n; i ++ ) {
        for (int j = 0; j < prime.size() && prime[j] * prime[j] <= i; j ++ ) {
            if (i % prime[j] == 0) {
                st[i] = true;
                break;
            }
        }
        if (st[i] == false) {
            prime.push_back(i);
        }
    }
}


// 埃氏筛
// @test-time:	53 ms
void ai_sieve(int n) {
    memset(st, false, sizeof st);
    prime.clear();
    if (n <= 1) {
        return;
    }

    st[1] = true;
    for (int i = 2; i <= n; i ++ ) {
        if (st[i] == false) {
            prime.push_back(i);
            for (int j = i + i; j <= n; j += i) {
                st[j] = true;
            }
        }
    }
}

// 欧拉筛,线性筛
// @test-time:
void euler_sieve(int n) {
    prime.clear();
    memset(st, false, sizeof st);
    if (n <= 1) {
        return;
    }

    for (int i = 2; i <= n; i ++ ) {
        if (st[i] == false) {
            prime.push_back(i);
        }
        for (int j = 0; prime[j] <= n / i; j ++ ) { // i 这个因数,分解后质因数小于等于 primes[j]
            st[i * prime[j]] = true;
            if (i % prime[j] == 0) {
                break;
            }
        }
    }
}


int main()
{
    scanf("%d", &n);

    euler_sieve(n);

    printf("%d\n", prime.size());
    return 0;
}

posted @ 2022-06-25 15:40  lucky_light  阅读(72)  评论(0编辑  收藏  举报