跳转至

质数筛

传统的质数判断方法采用双层遍历,对被判断数及其因数分别遍历。

bool is_prime[MAXN + 1];
vector<int> primes;

void sieve(){
    memset(is_prime, true, sizeof(is_prime));
    for (int i = 2; i <= MAXN; i++) {
        for (int j = 2; j <= sqrt(i); j++){
            if (i % j == 0) {
                is_prime[i] = false;
                break;
            }
        }
        if(is_prime[i]) primes.push_back(i);
    }
}

其时间复杂度达 \(O(n \sqrt{n})\) ,为提高效率,引入两种线性质数筛。

1 埃氏筛

埃氏筛从每个质因数出发,标记其倍数为合数。

bool is_prime[MAXN + 1];
vector<int> primes;

void eratosthenes() {
    memset(is_prime, true, sizeof(is_prime));
    is_prime[1] = false;
    for (int i = 2; i * i <= MAXN; ++i) {
        if (is_prime[i]) {
            for (int j = i * i; j <= MAXN; j += i) {
                is_prime[j] = false;
            }
        }
    }
    for (int i = 2; i <= MAXN; ++i) {
        if (is_prime[i]) {
            primes.push_back(i);
        }
    }
}

2 欧拉筛

欧拉筛标记合数时确保每个合数只被最小质因数标记,因此较埃氏筛相比效率更高。

bool is_prime[MAXN + 1];
vector<int> primes;

void euler_sieve() {
    memset(is_prime, true, sizeof(is_prime));
    is_prime[1] = false;
    for (int i = 2; i <= MAXN; ++i) {
        if (is_prime[i]) {
            primes.push_back(i);
        }
        for (int j = 0; j < primes.size(); j++) {
            if (i * primes[j] > MAXN) break;
            is_prime[i * primes[j]] = false;
            if (i % primes[j] == 0) break; // 关键:保证每个合数只被最小质因数筛掉
        }
    }
}

发现错误?想一起完善? 在 GitHub 上编辑此页

本文档内容作为个人算法笔记整理,代码模板可按需要参考和修改。