c++ 素数表的埃氏筛法和线性筛法总结

在 C++ 里,为了找出一定范围内的所有素数,埃氏筛法(埃拉托斯特尼筛法)和线性筛法(欧拉筛法)是两种常用的算法。下面为你详细总结这两种算法。

1. 素数定义
素数,也叫质数,是指一个大于 1 的自然数,除了 1 和它自身外,不能被其他自然数整除的数。例如 2、3、5、7 等都是素数。

2. 埃氏筛法(埃拉托斯特尼筛法)
原理
该算法的核心思想是从 2 开始,将每个素数的倍数标记为合数,逐步筛选出所有素数。具体来说,先假设所有数都是素数,然后从最小的素数 2 开始,把 2 的倍数(除 2 本身)都标记为合数;接着找到下一个未被标记的数,它就是素数,再把它的倍数标记为合数,依此类推,直到遍历完所有小于等于给定范围的数。

代码实现

#include <iostream>
#include <vector>

// 埃氏筛法求素数
std::vector<int> sieveOfEratosthenes(int n) {
    std::vector<bool> isPrime(n + 1, true);
    isPrime[0] = isPrime[1] = false;  // 0 和 1 不是素数

    for (int i = 2; i * i <= n; ++i) {
        if (isPrime[i]) {
            for (int j = i * i; j <= n; j += i) {
                isPrime[j] = false;
            }
        }
    }

    std::vector<int> primes;
    for (int i = 2; i <= n; ++i) {
        if (isPrime[i]) {
            primes.push_back(i);
        }
    }

    return primes;
}

int main() {
    int n = 30;
    std::vector<int> primes = sieveOfEratosthenes(n);

    std::cout << "Prime numbers up to " << n << " are: ";
    for (int prime : primes) {
        std::cout << prime << " ";
    }
    std::cout << std::endl;

    return 0;
}

复杂度分析

  • 时间复杂度:约为 O(n log log n)。这是因为对于每个素数 p,需要标记 n/p 个数为合数,对所有素数求和得到时间复杂度。
  • 空间复杂度:O(n),主要用于存储标记数组。

优缺点

  • 优点:实现简单,易于理解,对于较小范围的素数筛选效率较高。
  • 缺点:会有重复标记的情况,例如 6 会被 2 和 3 都标记一次,导致效率有一定损失。

3. 线性筛法(欧拉筛法)
原理

线性筛法在埃氏筛法的基础上进行了优化,保证每个合数只会被它的最小质因数筛去,从而避免了重复标记,实现了线性时间复杂度。具体做法是在遍历过程中,对于每个数 (i),都与已找到的素数表中的素数相乘,并标记乘积为合数,当 (i) 能被当前素数整除时就停止,这样可以确保每个合数只被其最小质因数筛一次。

代码实现

#include <iostream>
#include <vector>

// 线性筛法求素数
std::vector<int> eulerSieve(int n) {
    std::vector<bool> isPrime(n + 1, true);
    std::vector<int> primes;
    isPrime[0] = isPrime[1] = false;  // 0 和 1 不是素数

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

    return primes;
}

int main() {
    int n = 30;
    std::vector<int> primes = eulerSieve(n);

    std::cout << "Prime numbers up to " << n << " are: ";
    for (int prime : primes) {
        std::cout << prime << " ";
    }
    std::cout << std::endl;

    return 0;
}

复杂度分析

  • 时间复杂度:O(n),因为每个数只会被筛一次,避免了埃氏筛法的重复标记问题。
  • 空间复杂度:O(n),主要用于存储标记数组和素数表。

优缺点

  • 优点:时间复杂度为线性,效率高,尤其适用于大范围的素数筛选。
  • 缺点:代码实现相对复杂一些,理解起来有一定难度。

4. 总结

  • 埃氏筛法实现简单,对于较小范围的素数筛选足够高效,当对代码简洁性要求较高且范围不大时可优先选择。
  • 线性筛法时间复杂度更优,适合处理大范围的素数筛选问题,但实现相对复杂,在对性能要求极高的场景下使用更为合适。
埃氏筛法(Eratosthenes筛法)线性筛法(Linear Sieve)都是用于求解素数的算法。它们的主要区别在于筛选过程中的操作不同。 1. 埃氏筛法埃氏筛法是一种古老的求解素数的算法,由古希腊数学家埃拉托斯特尼(Eratosthenes)提出。它的基本思想是从2开始,将所有2的倍数标记为非素数,然后找到下一个未被标记的数(即3),将所有3的倍数标记为非素数,依此类推。最后留下的未被标记的数就是素数。 C++实现埃氏筛法的代码如下: ```cpp #include <iostream> #include <vector> using namespace std; const int N = 1000000;vector<int> is_prime(N, true); void sieve_of_eratosthenes() { is_prime = is_prime = false; for (int i = 2; i * i < N; ++i) { if (is_prime[i]) { for (int j = i * i; j < N; j += i) { is_prime[j] = false; } } } } int main() { sieve_of_eratosthenes(); for (int i = 2; i < N; ++i) { if (is_prime[i]) { cout << i << " "; } } return 0; } ``` 2. 线性筛法线性筛法是一种改进的埃氏筛法,它将筛选过程从平方根优化到线性时间。线性筛法的基本思想是对于每个素数p,筛选出所有小于等于p^2的合数。这样可以减少筛选的次数,提高效率。 C++实现线性筛法的代码如下: ```cpp #include <iostream> #include <vector> using namespace std; const int N = 1000000; vector<int> is_prime(N, true); vector<int> primes; void linear_sieve() { is_prime = is_prime = false; for (int p = 2; p * p < N; ++p) { if (is_prime[p]) { for (int i = p * p; i < N; i += p) { is_prime[i] = false; } } } for (int p = 2; p < N; ++p) { if (is_prime[p]) { primes.push_back(p); } } } int main() { linear_sieve(); for (int i = 0; i < primes.size(); ++i) { cout << primes[i] << " "; } return 0; } ``` 这两种算法都可以有效地求解素数,但线性筛法相对于埃氏筛法在筛选次数上有很大优势。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值