素数筛选中的两种经典方法——埃拉托色尼筛法(埃氏筛)和线性筛法-野牛程序员教少儿编程
作者:野牛程序员:2025-05-08 09:57:17算法阅读 1997
素数筛选中的两种经典方法——埃拉托色尼筛法(埃氏筛)和线性筛法-野牛程序员教少儿编程
🎯 素数大揭秘:埃氏筛 & 线性筛的魔法课堂
—— 青少年信息学竞赛入门系列 · 第①讲
🧠 什么是素数?
在数学的王国里,有一类数特别“孤傲”——它们除了 1 和自己,谁也不认识。
比如:
2、3、5、7、11、13……这些都是 素数(Prime Number)
而 4=2×2,6=2×3,9=3×3……这些叫 合数(Composite Number)
🧩 小测试:
以下哪些是素数?
👉 17,21,23,27,29
答案:✅ 17, 23, 29 是素数;❌ 21, 27 是合数
🔍 如何快速找出素数?
从 1 到 10 万中找素数,数一个算一个?太慢!
于是,数学家们发明了两种“批量筛选”的绝招:
🌀 第一招:埃拉托色尼筛法
(英文名:Eratosthenes Sieve)
📖 思想:
每当遇到一个素数,就把它的倍数全部划掉,留下的就是素数!
🎨 举个例子(1~30):
2 是素数 → 划掉 4,6,8,...
3 是素数 → 划掉 6,9,12,...
5 是素数 → 划掉 10,15,20,...
最后剩下的就是:2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ✅
✅ 方法一:埃拉托色尼筛法(Eratosthenes)
#include <iostream> #include <vector> #include <cmath> using namespace std; const int N = 1e6 + 10; // 筛选范围上限 bool is_prime[N]; // 标记数组,is_prime[i] == true 表示 i 是素数 // 埃氏筛法实现 void eratosthenes(int n) { fill(is_prime, is_prime + n + 1, true); // 初始化全部为 true is_prime[0] = is_prime[1] = false; // 0 和 1 不是素数 for (int i = 2; i <= sqrt(n); ++i) { if (is_prime[i]) { // 将 i 的所有倍数标记为非素数 for (int j = i * i; j <= n; j += i) { is_prime[j] = false; } } } } int main() { int n; cout << "请输入筛选素数的范围上限(如 1000000):"; cin >> n; eratosthenes(n); // 调用筛法 cout << "前100个素数为:" << endl; int count = 0; for (int i = 2; i <= n && count < 100; ++i) { if (is_prime[i]) { cout << i << " "; count++; } } cout << endl; return 0; }
⚡ 第二招:线性筛法(欧拉筛)
📖 思想:
每个合数,只被它的最小质因子标记一次,绝不重复!
🧠 什么是“最小质因子”?
比如:
6 = 2×3 → 最小质因子是 2
15 = 3×5 → 最小质因子是 3
77 = 7×11 → 最小质因子是 7
✅ 方法二:线性筛法(欧拉筛)
#include <iostream> #include <vector> using namespace std; const int N = 1e6 + 10; bool is_prime[N]; // is_prime[i] == false 表示 i 是素数 vector<int> primes; // 存储素数列表 // 线性筛实现:每个合数只被其最小质因子标记一次 void linear_sieve(int n) { for (int i = 2; i <= n; ++i) { if (!is_prime[i]) { primes.push_back(i); // i 是素数 } for (int p : primes) { if (p * i > n) break; is_prime[p * i] = true; // 标记合数 // 如果 p 是 i 的最小质因子,则跳出,保证每个合数只被标记一次 if (i % p == 0) break; } } } int main() { int n; cout << "请输入筛选素数的范围上限(如 1000000):"; cin >> n; linear_sieve(n); // 调用线性筛法 cout << "前100个素数为:" << endl; for (int i = 0; i < 100 && i < primes.size(); ++i) { cout << primes[i] << " "; } cout << endl; return 0; }
💡 亮点:
不重复标记,效率更高
时间复杂度是 $O(n),适合数据量超大的场景
🧮 对比总结表
对比项 | 埃氏筛法 | 线性筛法(欧拉筛) |
---|---|---|
重复标记 | 是 | 否 |
最坏效率 | $O(n log n) | $O(n) |
是否保留最小质因子 | 否 | ✅ 是 |
编码复杂度 | 简单 | 略复杂 |
使用场景 | 小中规模筛选 | 大规模竞赛常用 |
野牛程序员教少儿编程与信息学奥赛-微信|电话:15892516892
