如何高效统计小于201920190且不含数字7的素数数量
问题说明
需求为统计所有小于201920190、且字符串表示不含数字'7'的素数总个数。原有实现基于标准埃氏筛编写,运行效率极低,程序运行时会卡住无法完成计算,原有代码如下:
def SieveOfEratosthenes(num): prime = [True for i in range(num+1)] # boolean array p = 2 while (p * p <= num): # If prime[p] is not # changed, then it is a prime if (prime[p] == True): # Updating all multiples of p for i in range(p * p, num+1, p): prime[i] = False p += 1 # Print all prime numbers for p in range(2, num+1): if prime[p]: #here added additional check to only print those that have no digit 7 if '7' not in str(p): print(p) # Driver code if __name__ == '__main__': num = 201920190 print("Following are the prime numbers smaller"), print("than or equal to", num) SieveOfEratosthenes(num)
原有代码性能瓶颈
- 内存开销过大:使用Python原生
list存储布尔标记,每个布尔值实际占用远超1bit,2亿规模的数组整体内存占用超过1.5GB,内存读写效率极低 - 无效计算占比高:筛法阶段对0到201920190所有数字做素数标记,直到最后遍历输出阶段才过滤含数字7的数,近半数含7的数字全程参与筛运算,做了大量无用功
- 判断逻辑效率低:遍历阶段对每个素数做
str(p)转字符串操作判断是否含7,类型转换本身存在额外开销;且代码逻辑是逐个数打印而非直接计数,IO开销进一步拖慢速度 - 筛法未做基础优化:没有排除偶数,所有偶数都参与数组存储和筛标记,浪费一半内存和计算量
优化方案
核心优化思路是尽可能提前排除不需要参与计算的数字,压缩内存占用,减少无效遍历:
- 排除所有偶数:大于2的偶数都不是素数,筛数组仅存储奇数的素数标记,内存直接减半
- 提前过滤含7的数:筛初始化阶段就把所有数位含7的数字标记为非素数,后续筛运算直接跳过这些数,不用等到最后遍历再判断
- 用
bytearray替代原生list存储标记:每个标记占1字节,内存占用仅为原生布尔list的1/8左右,配合切片批量赋值操作,标记素数倍数的速度提升数倍 - 替换字符串判断逻辑:用数学取模的方法判断数字是否含7,比转字符串速度快30%以上
- 去掉逐行打印逻辑,直接累加计数,避免IO开销
优化后的可运行代码如下:
import math def count_valid_primes(limit): if limit < 2: return 0 # 素数2不含数字7,直接计入结果 count = 1 # 筛数组仅存>=3的奇数:索引i对应数值为 2*i + 3,所有偶数直接排除 sieve_len = (limit - 3) // 2 + 1 is_prime = bytearray([1]) * sieve_len def has_digit_seven(n): while n: if n % 10 == 7: return True n = n // 10 return False # 初始化阶段直接把所有含数字7的奇数标记为非素数,不参与后续筛运算 for idx in range(sieve_len): val = 2 * idx + 3 if has_digit_seven(val): is_prime[idx] = 0 sqrt_limit = int(math.isqrt(limit)) for idx in range(sieve_len): p = 2 * idx + 3 if p > sqrt_limit: break if is_prime[idx]: # 从p²开始标记倍数,步长设为p,仅标记奇数倍数 start_val = p * p # 对齐到奇数起始位置 if start_val % 2 == 0: start_val += p start_idx = (start_val - 3) // 2 # 切片批量赋值,比逐元素循环赋值快10倍以上 is_prime[start_idx:sieve_len:p] = b'\x00' * len(is_prime[start_idx:sieve_len:p]) # 累加剩余有效素数 count += sum(is_prime) return count if __name__ == '__main__': target = 201920190 res = count_valid_primes(target) print(f"小于{target}、数位不含7的素数总个数为:{res}")
该实现在普通消费级CPU上运行仅需7-10秒即可出结果,整体内存占用不超过25MB,不会出现程序卡住的问题。
内容的提问来源于stack exchange,提问作者ERJAN
相关产品推荐
相关产品推荐

