埃拉托色尼筛法 - 古希腊人的质数过滤器
埃拉托色尼筛法:2200年的质数过滤器
5W1H 发明者故事
Who(何人)- 发明者是谁?
发明者:Eratosthenes of Cyrene(埃拉托色尼,约公元前 276-194 年)
背景:
- 古希腊博学者,生于北非昔兰尼(今利比亚)
- 曾任亚历山大图书馆馆长
- 数学家、天文学家、地理学家、诗人,是历史上最早测量地球周长的人(误差仅约 2-3%)
- 亚里士多德的学生,柏拉图学派传人
现代分析贡献者:
- Donald E. Knuth 在 TAOCP 第二卷 4.5.4节详细分析了筛法的时间复杂度 O(N log log N)
- Legendre 于 1808 年给出了筛法时间复杂度的早期分析
当时的处境:古希腊数学家对质数(素数)充满兴趣,欧几里得已在《几何原本》中证明了质数有无穷多个。埃拉托色尼需要一种系统方法来列举所有质数。
When(何时)- 什么时候发明的?
时间:约公元前 240 年
时代背景:
- 希腊化时代(Hellenistic Period),亚历山大大帝的帝国刚刚分裂
- 亚历山大图书馆是当时世界上最大的知识中心,收藏了数十万卷手稿
- 数学以几何为主流,算术和数论尚在发展中
- 没有代数符号体系,所有数学用文字和几何图形表达
- 算法的概念还不存在,但埃拉托色尼的方法已经具备了现代算法的特征:清晰的步骤、有限的操作、确定的终止
Where(何地)- 在哪里发明的?
地点:埃及亚历山大城(Alexandria),亚历山大图书馆
环境:
- 亚历山大图书馆是公元前 3 世纪的"知识工厂",雇佣了众多学者从事研究和抄录
- 地中海沿岸,希腊、埃及、巴比伦三种文明的知识在此汇聚
- 无纸时代,数学推导写在莎草纸(papyrus)上
- 埃拉托色尼在此工作了约 50 年,晚年因眼盲而绝食身亡
What(何事)- 发明了什么?
算法:埃拉托色尼筛法(Sieve of Eratosthenes)
核心概念:从 2 开始,将所有 2 的倍数标记为"非质数",然后找下一个未标记的数(必为质数),将其所有倍数标记为"非质数",重复直到 √N。最终未被标记的数就是质数。就像用筛子(sieve)过滤,每次筛掉一种合数的倍数,留下的是质数。
关键突破:
- 主动标记合数,而非逐个测试每个数是否为质数
- 每个合数只被其最小质因子"筛掉"(实际上现代优化版本中)
- 时间复杂度 O(N log log N),空间 O(N)
- 外层循环只需到 √N(因为大于 √N 的合数必有小于 √N 的质因子)
Why(何因)- 为什么发明?
要解决的问题:
- 系统列举质数:古希腊数学家需要知道到某个数为止有哪些质数
- 验证数论猜想:研究质数分布规律需要大量具体的质数数据
- 计算需要:古代天文学计算需要分解大数,而分解需要质数表
当时的挑战:
- 没有算法概念,只有几何证明方法
- 大数的计算极其费时(用算盘或手工计算)
- 没有负数和零的概念,需要绕过这些限制
- 如何证明算法的正确性和终止性
动机:在编纂数学知识的过程中,埃拉托色尼需要一个系统的方法来列举质数,供数论研究使用。他用一张数字表格,逐步划掉合数,就像用筛子过滤一样。
How(何果)- 如何实现?有什么影响?
算法步骤(现代描述):
1. 创建布尔数组 is_prime[0..N],初始全为 true
2. is_prime[0] = is_prime[1] = false(0和1不是质数)
3. for p = 2 to √N:
若 is_prime[p] 为 true(p是质数):
for j = p² to N, step p:
is_prime[j] = false(标记 p 的倍数为合数)
注意:从 p² 开始(小于 p² 的 p 的倍数已被更小的质数筛掉)
4. 所有 is_prime[i]=true 的 i 就是质数
历史影响:
- 保持了 2200 年的"最佳算法"地位(在朴素方法中),这在算法史上极为罕见
- 现代变体(分段筛、线性筛)仍以此为基础
- 密码学(RSA质数生成)和数论研究的基础工具
- 编程竞赛中的标准算法,几乎每个程序员都实现过
- 教学中用于说明算法效率和"筛法思想"(如 Eratosthenes → 线性筛 → 轮筛)
实际性能:
N=10^6:找到 78498 个质数,约 10^6 次操作
N=10^8:找到 5761455 个质数,约 3×10^8 次操作(log log N ≈ 3)
名言:Knuth 在 TAOCP 中写道:“埃拉托色尼筛法是历史上最古老的算法之一,也是最优雅的算法之一。它的基本思想在 2000 年后仍未被超越。”
自然语言需求定义
需求名称:实现埃拉托色尼筛法,生成 N 以内所有质数
功能需求(用精确的中文描述)
-
创建筛(sieve):分配并初始化布尔数组
- 输入:上界 N
- 操作:calloc 分配 N+1 个字节,初始值全为1(true)
- 输出:bool 数组指针
-
执行筛法:标记所有合数
- 输入:bool 数组、上界 N
- 操作:从 p=2 到 √N,若 is_prime[p] 为真,从 p² 开始标记 p 的倍数为假
- 输出:无(原地修改数组)
-
收集质数:将所有未标记的数收集到结果数组
- 输入:bool 数组、上界 N、结果数组指针、结果数量指针
- 操作:遍历数组,收集所有 is_prime[i]=true 的 i
- 输出:质数数组和数量
-
释放内存:释放筛数组
- 输入:bool 数组
- 输出:无
约束条件
- N 可以达到 10^7(需要约 10MB 内存)
- 从 p² 开始标记(优化:跳过已被标记的小倍数)
- 外层循环只到 √N
- 正确处理 N < 2 的边界情形(没有质数)
验收标准(必须可验证)
| 编号 | 测试场景(自然语言描述) | 预期结果 | 验证方式 |
|---|---|---|---|
| 1 | N=30 时,前10个质数 | [2,3,5,7,11,13,17,19,23,29] | 逐一比较 |
| 2 | N=100 时,质数数量 | 25 个 | 计数验证 |
| 3 | N=1000 时,质数数量 | 168 个 | 计数验证 |
| 4 | N=10000 时,第100个质数 | 541 | 索引验证 |
| 5 | 质数的正确性(无遗漏、无误判) | 所有结果确实是质数 | 用试除法验证前20个 |
| 6 | N=1(边界) | 0个质数 | 计数为0 |
AI 生成提示
基于以上需求和验收标准,用标准C语言实现埃拉托色尼筛法。
要求:
1. 使用标准C99
2. 使用char数组(0/1)作为筛,比bool数组更节省内存
3. sieve_create(N) → char* / sieve_run(sieve, N) / sieve_get_primes(sieve, N, &count)
4. 外层循环到 sqrt(N),内层从 p*p 开始
5. 在main中实现全部6个验收标准
6. 测试通过输出 "✓ 测试X通过",失败输出 "✗ 测试X失败"
核心函数:
- sieve_of_eratosthenes(N, &count) → int*(返回质数数组,调用者free)
- is_prime_trial(n) → bool(试除法,用于验证)
C语言实现文件
对应文件: sieve_of_eratosthenes.c
编译运行:
gcc -std=c99 -Wall -o sieve_test sieve_of_eratosthenes.c -lm
./sieve_test
核心函数:
sieve_of_eratosthenes(N, count)- 返回 N 以内所有质数的数组is_prime_trial(n)- 试除法(验证用)
239

被折叠的 条评论
为什么被折叠?



