素数筛

埃氏筛

原理:每找到一个素数,便将以该素数为因子的所有合数标记,最终未被标记的数即为素数。
复杂度:据说是 \(O(n\log\log n)\)

#include <bits/stdc++.h>
using namespace std;

const int N = 1e8+1.5, M = 6e6;
bool vis[N];
int prime[M];

void getPrime()
{
    // int times = 0; // 记录进入内循环的次数
    int &cnt = prime[0];
    int i;
    for (i = 2; i * i < N; ++i) {
        if (vis[i]) continue;
        prime[++cnt] = i;
        for (int j = i * i; j < N; j += i) {
            vis[j] = 1; // ++times;
        }
    }

    for (; i < N; ++i) {
        if (!vis[i]) prime[++cnt] = i;
    }

    // cout << cnt << endl; // 5761455
    // cout << times << endl; // 242570204
}

int main()
{
    getPrime();
    cout << "1e9 内共 " << prime[0] << " 个素数" << endl;
    return 0;
}

/*
运行时间:约1.2s
*/

欧拉筛

原理:每遍历到一个数,便将 \(i * prime[j]\) 标记,同时保证 \(prime[j]\) 是其最小质因子,从而保证每个合数只被标记一次。

复杂度:\(O(n)\)

#include <bits/stdc++.h>
using namespace std;

const int N = 1e8+1.5, M = 6e6;
bool vis[N];
int prime[M];

void getPrime()
{
    // int times = 0; // 记录进入内循环的次数
    int &cnt = prime[0];
    for (int i = 2; i < N; ++i) {
        if (!vis[i]) prime[++cnt] = i;
        for (int j = 1; j <= cnt && i * prime[j] < N; ++j) {
            vis[i * prime[j]] = 1; // ++times;
            if (i % prime[j] == 0) break;
        }
    }

    // cout << cnt << endl; // 5761455
    // cout << times << endl; // 94238544
}

int main()
{
    getPrime();
    cout << "1e9 内共 " << prime[0] << " 个素数" << endl;
    return 0;
}

/*
运行时间:约0.61s
5761455 + 94238544 = 99999999, 每个合数恰好被标记一次
*/

img

学以致用

有了素数筛,我们能干什么呢?

首先让我们来欣赏一下集智慧美貌于一身的诸葛大力吧。

271是第58个质数从7开始11个连续质数之和

“271是第58个质数”

“从7开始11个连续质数之和”

大力说得究竟对不对呢?就让掌握了素数筛的我们来实践检验一下吧。

改写主函数如下:

void think(char *wordsInHeart){}
int main()
{
    getPrime();

    /** 验证“271是第58个质数”*/
    cout << "第58个素数是: " << prime[58] << endl;

    if (271 == prime[58]) think("不愧是诸葛大力!");
    else {
        puts("疑?大力怎么会错呢?一定是有bug!");
        exit(0);
    }

    /** 验证“271是从7开始11个连续质数之和”*/
    int pos = 1;
    while (prime[pos] != 7) ++pos;  // 寻找素数7所在位置
    int sum = 0;
    for (int i = 0; i < 11; ++i) {
        cout << (i?" + ":"") << prime[pos+i];
        sum += prime[pos+i];
    }
    cout << " = " << sum << endl;

    if (271 == sum) printf("哇!大力好厉害!");
    else puts("疑?大力怎么会错呢?一定是有bug!");
    return 0;
}

得到结果如下:

小试牛刀

洛谷 P3383 【模板】线性筛素数

洛谷 P1835 素数密度

posted @ 2020-11-18 23:49  Zewbie  阅读(151)  评论(0)    收藏  举报