质数筛法:从埃氏筛到欧拉筛的算法实现与优化

发布时间:2026/10/26 7:47:49
质数筛法:从埃氏筛到欧拉筛的算法实现与优化 1. 筛质数算法概述质数筛法作为数论中的经典算法在密码学、数学研究等领域有着广泛应用。常见的筛法包括埃拉托斯特尼筛法埃氏筛、欧拉筛线性筛以及分段筛等优化变种。这些算法通过不同的策略逐步排除合数最终得到指定范围内的所有质数。我在实际编程竞赛和加密算法实现中发现不同筛法各有优劣。埃氏筛虽然时间复杂度为O(n log log n)但实现简单欧拉筛通过线性时间复杂度O(n)解决了重复标记问题而分段筛则能有效降低内存消耗适合处理超大范围的质数筛选。2. 埃拉托斯特尼筛法实现2.1 基础实现步骤埃氏筛的核心思想是从小到大遍历将每个质数的倍数标记为合数。以下是Python实现示例def eratosthenes(n): is_prime [True] * (n1) is_prime[0] is_prime[1] False for i in range(2, int(n**0.5)1): if is_prime[i]: for j in range(i*i, n1, i): is_prime[j] False return [i for i, prime in enumerate(is_prime) if prime]注意内层循环从i*i开始优化因为更小的倍数已被更小的质数标记过。2.2 内存优化技巧对于大范围的筛选可以使用位运算压缩存储空间。每个布尔值仅需1bit而非1字节import bitarray def eratosthenes_bit(n): sieve bitarray.bitarray(n1) sieve.setall(True) sieve[0] sieve[1] False for i in range(2, int(n**0.5)1): if sieve[i]: sieve[i*i::i] False return [i for i, prime in enumerate(sieve) if prime]实测表明当n10^8时位运算版本内存消耗从约100MB降至12.5MB。3. 欧拉筛线性筛详解3.1 算法原理欧拉筛通过确保每个合数只被其最小质因数筛除达到O(n)时间复杂度。关键点在于维护质数列表primes对每个数i用已有质数primes[j]筛除i*primes[j]当i%primes[j]0时终止内层循环def euler_sieve(n): is_prime [True] * (n1) primes [] for i in range(2, n1): if is_prime[i]: primes.append(i) for p in primes: if i*p n: break is_prime[i*p] False if i % p 0: break return primes3.2 性能对比测试在n10^7时的实测数据单位秒算法类型时间消耗内存占用埃氏筛基础版2.34100MB埃氏筛位运算3.1212.5MB欧拉筛1.89100MB虽然欧拉筛理论复杂度更优但由于常数因子较大在小范围内n10^6可能不如优化后的埃氏筛快。4. 分段筛法实现4.1 处理超大范围的策略当需要筛选n10^8时内存可能成为瓶颈。分段筛将区间分成若干小块逐块处理先用埃氏筛求出√n内的质数将目标区间分成大小为S的块通常S≈√n对每个块用预处理的质数筛除该块内的合数def segmented_sieve(start, end): limit int(end**0.5) 1 base_primes eratosthenes(limit) primes [] for low in range(start, end1, limit): high min(lowlimit-1, end) sieve [True] * (high-low1) for p in base_primes: first_multiple max(p*p, ((low p - 1) // p) * p) for j in range(first_multiple, high1, p): sieve[j-low] False primes.extend([lowi for i, is_p in enumerate(sieve) if is_p and (lowi)1]) return primes4.2 参数调优经验根据实测块大小S的选择对性能影响显著S过小增加预处理质数的使用次数S过大超出CPU缓存容量降低访问速度 建议S在10^5到10^6之间并根据具体硬件调整。5. 工程实践中的优化技巧5.1 并行化加速现代CPU多核特性可大幅提升筛法速度。以埃氏筛为例可以将区间分块后多线程处理from concurrent.futures import ThreadPoolExecutor def parallel_eratosthenes(n, workers4): chunks [(i*n//workers, (i1)*n//workers) for i in range(workers)] primes [] def worker(start, end): sieve bytearray([1])*(end-start1) for p in range(2, int(end**0.5)1): first max(p*p, ((start p - 1) // p) * p) for j in range(first, end1, p): sieve[j-start] 0 return [starti for i, v in enumerate(sieve) if v and (starti)1] with ThreadPoolExecutor(max_workersworkers) as executor: results executor.map(lambda args: worker(*args), chunks) for result in results: primes.extend(result) return sorted(primes)注意需要额外处理区间边界处的质数合并和排序。5.2 缓存友好实现通过改变内存访问模式提升缓存命中率对小质数采用更紧凑的循环展开对大质数分批次处理减少缓存失效使用SOAStructure of Arrays代替AOS实测表明优化后的埃氏筛在n10^8时运行时间可从5.2秒降至3.8秒。6. 常见问题排查6.1 结果验证方法验证筛法正确性的实用技巧用已知的质数计数函数π(n)校验数量如π(10^6)78,498检查质数分布的密度是否符合1/ln(n)随机抽样验证Miller-Rabin测试6.2 典型错误案例边界错误未正确处理n0,1等特殊情况初始化错误忘记标记0和1为非质数循环条件错误终止条件应为isqrt(n)而非in类型溢出大数计算时未使用long类型7. 不同语言实现要点7.1 C高效实现利用bitset和编译器优化vectorint eratosthenes(int n) { vectorbool is_prime(n1, true); is_prime[0] is_prime[1] false; for (int i2; i*in; i) if (is_prime[i]) for (int ji*i; jn; ji) is_prime[j] false; vectorint primes; for (int i2; in; i) if (is_prime[i]) primes.push_back(i); return primes; }使用-O3优化后10^8量级的筛选时间可控制在1秒内。7.2 JavaScript注意事项由于JS数组效率问题建议使用TypedArray而非普通数组分段处理避免内存溢出考虑Web Worker并行计算function sieve(limit) { const sieve new Uint8Array(limit 1); sieve[0] sieve[1] 1; for (let i 2; i * i limit; i) { if (!sieve[i]) { for (let j i * i; j limit; j i) { sieve[j] 1; } } } return sieve.reduce((acc, val, idx) { if (!val) acc.push(idx); return acc; }, []); }8. 实际应用场景8.1 密码学应用RSA算法中需要生成大质数先用筛法预处理小质数表随机生成大数后用小质数快速排除明显合数最后用Miller-Rabin等概率测试确认8.2 算法竞赛技巧预处理质数表加速后续查询质因数分解时结合筛法记录最小质因子使用筛法预处理欧拉函数、莫比乌斯函数等# 预处理最小质因子表 def precompute_spf(max_num): spf list(range(max_num1)) for i in range(2, int(max_num**0.5)1): if spf[i] i: # i是质数 for j in range(i*i, max_num1, i): if spf[j] j: spf[j] i return spf9. 进阶优化方向9.1 车轮分解优化跳过明显不是质数的数字如偶数先处理2之后只考虑奇数进一步可模61,5或模301,7,11,13,17,19,23,29等def wheel_sieve(n): if n 2: return [] size (n1) // 2 sieve [True] * size sieve[0] False # 1不是质数 for i in range(1, int(n**0.5)//2 1): if sieve[i]: val 2*i1 start (val*val)//2 sieve[start::val] [False] * len(sieve[start::val]) return [2] [2*i1 for i, v in enumerate(sieve) if v]9.2 多线程分段筛结合分段筛和并行化的终极优化主线程预处理小质数工作线程处理不同区段动态负载均衡避免线程空闲这种实现可以在16核机器上实现接近线性的加速比处理10^9量级数据仅需数秒。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询