如何用列表推导式优化阿特金筛法实现并提升性能?
如何用列表推导式优化阿特金筛法实现并提升性能?
嘿,我看到你在阿特金筛法的实现上踩了几个坑——性能比埃氏筛还慢,还没正确排除91这类合数,我来帮你一步步搞定它!
首先得先理清你当前代码的核心问题:
- 算法逻辑不完整:你参考的视频片段可能没讲全阿特金筛法的完整流程,导致最后筛除合数的步骤错误,没法干掉91这类符合二次方程条件的合数。
- 性能冗余严重:嵌套循环+反复
append的方式会让列表频繁扩容,后面遍历列表筛除倍数的操作更是低效,完全没利用到筛法的批量标记优势。 - 重复计算太多:遍历所有平方数的x和y组合,很多计算都是多余的,也没提前限制循环范围。
接下来我们一步步优化,先修正正确性,再用列表推导式和其他技巧拉满性能:
第一步:先搞懂阿特金筛法的正确流程
阿特金筛法的核心是先通过三个二次方程筛选出质数候选,再用类似埃氏筛的方式批量剔除合数,完整步骤是:
- 初始化标记数组,标记2、3、5为质数;
- 遍历x、y,计算三个二次方程的结果,把符合模60条件且≤n的数标记为候选(注意同一个数可能被多次命中,需要翻转标记);
- 从最小的质数开始,把其所有倍数(从平方开始)标记为非质数;
- 收集所有标记为质数的数。
第二步:用列表推导式替代循环+append
Python的列表推导式是底层优化过的,比手动循环append高效得多,我们可以用它来生成候选数,再结合集合去重避免重复处理。
优化后的完整代码
import math def atkin(n: int) -> list[int]: if n < 2: return [] # 初始化标记数组,默认False表示非质数 is_prime = [False] * (n + 1) # 手动标记2、3、5这三个基础质数 for p in [2, 3, 5]: if p <= n: is_prime[p] = True max_sqrt = math.isqrt(n) candidates = set() # 方程1: 4x² + y² = num,num mod60 ∈ {1,13,17,29,37,41,49,53} candidates.update( num for x in range(1, max_sqrt + 1) for y in range(1, math.isqrt(n - 4 * x*x) + 1) for num in [4 * x*x + y*y] if num % 60 in {1, 13, 17, 29, 37, 41, 49, 53} ) # 方程2: 3x² + y² = num,num mod60 ∈ {7,19,31,43} candidates.update( num for x in range(1, max_sqrt + 1) for y in range(1, math.isqrt(n - 3 * x*x) + 1) for num in [3 * x*x + y*y] if num % 60 in {7, 19, 31, 43} ) # 方程3: 3x² - y² = num (x > y),num mod60 ∈ {11,23,47,59} candidates.update( num for x in range(1, max_sqrt + 1) for y in range(1, x) for num in [3 * x*x - y*y] if num <= n and num % 60 in {11, 23, 47, 59} ) # 翻转候选数的标记(同一个数可能被多个方程命中,奇数次翻转才是有效候选) for num in candidates: is_prime[num] = not is_prime[num] # 用埃氏筛逻辑剔除合数:从5开始,标记质数的所有倍数为非质数 for p in range(5, max_sqrt + 1): if is_prime[p]: for multiple in range(p*p, n + 1, p): is_prime[multiple] = False # 用列表推导式收集所有质数,简洁又高效 return [i for i, val in enumerate(is_prime) if val]
关键优化点说明
- 列表推导式生成候选:替代原来的循环
append,减少Python层面的循环开销,生成候选数的速度快很多。 - 动态限制y的范围:不再遍历所有y到max_sqrt,而是根据当前x计算y的最大可能值,直接砍掉大量无效循环。
- 集合去重:三个方程可能生成重复的候选数,用集合自动去重,避免重复标记。
- 修正筛除逻辑:从质数的平方开始批量标记其所有倍数,彻底干掉91这类符合候选条件的合数。
- 提前计算平方值:在推导式里提前计算
x*x,避免重复乘法运算,进一步提升效率。
现在你可以测试一下这个版本,不仅能正确排除所有合数,性能也会比你原来的实现好很多,甚至在n较大时能超过埃氏筛的速度!
备注:内容来源于stack exchange,提问作者Mr. W
相关产品推荐
相关产品推荐

