|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: "信息奥赛一本通函数题库解析-2: 素数个数" |
| 4 | +date: 2024-07-03 00:36:24 +0800 |
| 5 | +categories: 奥赛 信息奥赛 函数题库 |
| 6 | +--- |
| 7 | + |
| 8 | +原题链接: [素数个数](http://ybt.ssoier.cn:8088/problem_show.php?pid=1151) |
| 9 | + |
| 10 | +## 解题思路 |
| 11 | + |
| 12 | +这道题目要求我们计算从 2 到 n(其中 n 的最大值为 50000)之间的素数个数。素数是指除了 1 和自身以外不再有其他因数的自然数。解决这个问题我们可以使用 **埃拉托色尼筛法(Sieve of Eratosthenes)**,这是一种高效的计算素数的算法。 |
| 13 | + |
| 14 | +### 埃拉托色尼筛法解释 |
| 15 | + |
| 16 | +埃拉托色尼筛法是一种古老的算法,用于寻找一个范围内的所有素数。其基本思想是从 2 开始,将每个素数的倍数标记为合数(即非素数)。具体步骤如下: |
| 17 | + |
| 18 | +1. **初始化**:创建一个布尔数组 `is_prime`,大小为 n+1,并将所有元素初始化为 `true`。`is_prime[i]` 表示数 i 是否为素数。 |
| 19 | +2. **标记合数**:从 2 开始,对于每个数 i,如果 `is_prime[i]` 为 `true`,则将 i 的所有倍数标记为 `false`。 |
| 20 | +3. **计数素数**:遍历数组 `is_prime`,统计值为 `true` 的元素个数,这些元素对应的索引即为素数。 |
| 21 | + |
| 22 | +### 算法的时间复杂度 |
| 23 | + |
| 24 | +埃拉托色尼筛法的时间复杂度为 \(O(n \log \log n)\),对于 n 最大值为 50000 的情况,效率是非常高的。 |
| 25 | + |
| 26 | +### 代码实现 |
| 27 | + |
| 28 | +下面是使用埃拉托色尼筛法计算从 2 到 n 范围内素数个数的代码: |
| 29 | + |
| 30 | +```cpp |
| 31 | +#include <iostream> |
| 32 | +#include <algorithm> // 引入算法库以使用 fill_n |
| 33 | +using namespace std; |
| 34 | + |
| 35 | +// 函数:使用埃拉托色尼筛法计算素数的个数 |
| 36 | +int count_primes(int n) { |
| 37 | + if (n < 2) return 0; // 如果 n 小于 2,则没有素数 |
| 38 | + |
| 39 | + // 创建布尔数组并初始化 |
| 40 | + bool is_prime[n + 1]; |
| 41 | + fill_n(is_prime, n + 1, true); // 初始化数组为 true |
| 42 | + |
| 43 | + is_prime[0] = is_prime[1] = false; // 0 和 1 不是素数 |
| 44 | + |
| 45 | + // 使用埃拉托色尼筛法 |
| 46 | + for (int i = 2; i * i <= n; ++i) { |
| 47 | + if (is_prime[i]) { |
| 48 | + for (int j = i * i; j <= n; j += i) { |
| 49 | + is_prime[j] = false; // 标记 i 的倍数为非素数 |
| 50 | + } |
| 51 | + } |
| 52 | + } |
| 53 | + |
| 54 | + // 统计素数的个数 |
| 55 | + int count = 0; |
| 56 | + for (int i = 2; i <= n; ++i) { |
| 57 | + if (is_prime[i]) { |
| 58 | + count++; // 如果是素数,计数加一 |
| 59 | + } |
| 60 | + } |
| 61 | + |
| 62 | + return count; // 返回素数的总个数 |
| 63 | +} |
| 64 | + |
| 65 | +int main() { |
| 66 | + int n; |
| 67 | + cin >> n; // 输入 n |
| 68 | + |
| 69 | + int prime_count = count_primes(n); // 计算素数的个数 |
| 70 | + |
| 71 | + cout << prime_count << endl; // 输出素数的个数 |
| 72 | + |
| 73 | + return 0; |
| 74 | +} |
| 75 | +``` |
| 76 | +
|
| 77 | +### 代码解析 |
| 78 | +
|
| 79 | +1. **初始化布尔数组**:`bool is_prime[n + 1];` 创建一个大小为 n+1 的布尔数组,并使用 `fill_n(is_prime, n + 1, true);` 初始化所有元素为 `true`。 |
| 80 | +2. **特殊处理 0 和 1**:`is_prime[0] = is_prime[1] = false;` 将 0 和 1 标记为非素数。 |
| 81 | +3. **筛选素数**:使用两层循环,外层循环从 2 到 \(\sqrt{n}\),内层循环从 i\(^2\) 开始,将每个素数 i 的所有倍数标记为 `false`。 |
| 82 | +4. **统计素数**:最后遍历数组 `is_prime`,统计所有为 `true` 的元素个数。 |
| 83 | +
|
| 84 | +这样就能高效地计算从 2 到 n 之间的素数个数并输出结果。 |
0 commit comments