如何进一步优化我的Python 3埃拉托斯特尼筛法代码?
埃拉托斯特尼筛法Python代码优化方案
你的原代码在计算2000000以内素数时耗时约3.36秒,主要瓶颈在于频繁的列表查找、拼接操作以及线性时间的边界计算,以下是针对性的优化方法:
原代码核心问题分析
nums.index()的线性查找开销:每次调用都会遍历列表找元素位置,时间复杂度O(n),循环中多次调用会累积大量耗时。- 列表拼接与重新赋值:
nums = nums[0:...] + [...]会创建新列表,涉及内存拷贝,效率极低。 - 每次循环计算
max(nums):每次取最大值都要遍历列表,额外增加O(n)时间。 - 初始列表构建效率低:逐个
append元素不如直接生成结构高效的序列。
优化方案1:使用布尔数组(经典高效实现)
布尔数组是筛法的标准实现方式,内存占用小,标记操作批量完成,避免了列表的频繁修改:
import time def sieve(n): if n < 2: return [] # 初始化布尔数组,索引对应数字,值表示是否为素数 is_prime = [True] * (n + 1) is_prime[0] = is_prime[1] = False # 预计算平方根边界,避免重复计算 sqrt_n = int(n ** 0.5) + 1 for i in range(2, sqrt_n): if is_prime[i]: # 批量标记i的倍数为非素数 is_prime[i*i : n+1 : i] = [False] * len(is_prime[i*i : n+1 : i]) # 收集所有素数 primes = [i for i, val in enumerate(is_prime) if val] return primes st = time.time() primes = sieve(2000000) et = time.time() print(f"运行时间: {et - st:.6f}秒")
该实现针对2000000的计算耗时通常在0.1秒以内,效率提升非常明显。
优化方案2:仅处理奇数(进一步节省空间与计算)
除了2以外,所有偶数都不是素数,因此可以只存储奇数,将内存占用和循环次数减少一半:
import time def sieve_optimized(n): if n < 2: return [] primes = [2] if n == 2: return primes # 计算奇数的数量,索引i对应数字2*i+1 size = (n - 1) // 2 is_prime = [True] * size sqrt_n = int(n ** 0.5) // 2 for i in range(sqrt_n): if is_prime[i]: # 计算当前奇数对应的起始标记位置 current_prime = 2 * i + 1 start = 2 * i * (i + 1) # 批量标记current_prime的奇数倍数为非素数 is_prime[start::current_prime] = [False] * len(is_prime[start::current_prime]) # 将索引转换为实际素数 primes += [2*i+1 for i, val in enumerate(is_prime) if val] return primes st = time.time() primes = sieve_optimized(2000000) et = time.time() print(f"运行时间: {et - st:.6f}秒")
该版本的内存占用仅为布尔数组版的一半,运行速度会比基础布尔数组版再快10%-20%。
额外优化细节
- 预计算边界:提前算出
int(n**0.5),避免循环中重复计算。 - 原地修改数据:使用布尔数组的切片赋值,避免创建新列表的内存拷贝开销。
- 局部变量优先:在函数内使用局部变量(比如
sqrt_n),减少全局查找的开销。
内容的提问来源于stack exchange,提问作者Layan Jethwa
相关产品推荐
相关产品推荐

