如何为埃拉托斯特尼筛法(Sieve of Erathosthenes)实现支持增量更新与范围复用的缓存机制
如何为埃拉托斯特尼筛法(Sieve of Erathosthenes)实现支持增量更新与范围复用的缓存机制
当然可以实现!你想要的这种支持增量扩展、范围复用的缓存机制,刚好能完美适配埃拉托斯特尼筛法的特性,我来给你详细拆解实现思路和代码:
核心思路
我们需要维护一组缓存状态:
- 已筛过的最大数值
max_cached - 对应筛法的布尔数组
sieve_cache(和你原代码一样,只存储奇数的标记,节省空间) - 已经生成的质数列表
primes_cache
每次调用sieve(n)时:
- 如果
n小于等于max_cached:直接从缓存的质数列表中切片,返回所有小于n的质数 - 如果
n大于max_cached:- 若第一次调用,直接用原筛法逻辑生成到
n并初始化缓存 - 若非第一次,增量扩展筛数组,只处理新增范围内需要标记的倍数,然后收集新增的质数更新缓存
- 若第一次调用,直接用原筛法逻辑生成到
具体实现代码
from math import sqrt def cached_sieve(): # 闭包内部维护缓存状态,避免全局变量污染 max_cached = 0 sieve_cache = [] primes_cache = [] def sieve(n): nonlocal max_cached, sieve_cache, primes_cache # 处理边界情况 if n <= 2: return () if n <= 2 else (2,) # 情况1:当前n小于等于已缓存的最大值,直接从缓存切片 if n <= max_cached: # 找到第一个大于等于n的质数位置,切片返回 idx = 0 while idx < len(primes_cache) and primes_cache[idx] < n: idx += 1 return tuple(primes_cache[:idx]) # 情况2:需要扩展筛子到n if max_cached == 0: # 第一次调用,初始化筛子(和原代码逻辑一致) sieve_cache = [True] * (n // 2) for i in range(3, int(sqrt(n)) + 1, 2): if sieve_cache[i // 2]: sieve_cache[i*i//2::i] = [False] * ((n - i*i - 1) // (2*i) + 1) primes_cache = [2] + [2*i + 1 for i in range(1, n//2) if sieve_cache[i]] max_cached = n return tuple(primes_cache) else: # 扩展筛数组:计算需要新增的长度 new_total_length = (n - 1) // 2 sieve_cache.extend([True] * (new_total_length - len(sieve_cache))) # 只处理从原max_cached的平方根到新n的平方根之间的奇数 start_i = max(3, int(sqrt(max_cached)) + 1) # 确保起始i是奇数 if start_i % 2 == 0: start_i += 1 for i in range(start_i, int(sqrt(n)) + 1, 2): if sieve_cache[i // 2]: # 标记倍数的起始位置:取i²和max_cached+1中的较大值,且保证是奇数 start = max(i*i, max_cached + 1) if start % 2 == 0: start += 1 start_idx = start // 2 # 计算需要标记的元素数量,批量赋值为False sieve_cache[start_idx::i] = [False] * ((n - start - 1) // (2*i) + 1) # 收集新增的质数(从max_cached之后的奇数中筛选) new_primes_start = ((max_cached - 1) // 2) + 1 new_primes = [2*i + 1 for i in range(new_primes_start, n//2) if sieve_cache[i]] primes_cache.extend(new_primes) max_cached = n return tuple(primes_cache) return sieve # 使用示例 sieve = cached_sieve()
使用测试
你可以这样测试缓存的效果:
# 第一次调用,生成并缓存小于10的质数 print(sieve(10)) # 输出: (2, 3, 5, 7) # 调用n=5,直接复用缓存切片 print(sieve(5)) # 输出: (2, 3) # 调用n=20,增量扩展筛子到20,新增11,13,17,19 print(sieve(20)) # 输出: (2, 3, 5, 7, 11, 13, 17, 19)
关键细节说明
- 用闭包维护缓存,避免了全局变量的副作用,同时保持状态的持续性
- 增量扩展时只处理必要的奇数范围,不会重复计算已筛过的部分,保证了效率
- 完全兼容你原代码的空间优化逻辑(只存储奇数的标记),不会额外增加内存开销
备注:内容来源于stack exchange,提问作者jonnybolton16
相关产品推荐
相关产品推荐

