大区间素数求解器优化:非零起始区间素数生成加速问询
大区间素数生成优化问题
我需要生成从非零起始值的大区间素数,比如从2^64到2^64+1,000,000。由于不需要生成从0开始的所有素数,传统筛法速度更慢且占用内存更高。我已经实现了多线程版本,现在提供单线程代码以便优化,请问:
- 能否通过矩阵并行化来更快地检测素性?
- 还有哪些加速方法?
以下是我以2^32到2^32+1,000,000为例的单线程实现代码:
import gmpy2 import timeit def primes_01(start, end): last_six_divisor_1 = start - (start % 6) + 1 last_six_divisor_2 = start - (start % 6) + 5 while last_six_divisor_1 < start: last_six_divisor_1 += 6 while last_six_divisor_2 < start: last_six_divisor_2 += 6 candidates_1 = [i for i in range(last_six_divisor_1, end, 6) if gmpy2.is_prime(i)] candidates_2 = [i for i in range(last_six_divisor_2, end, 6) if gmpy2.is_prime(i)] candidates = [i for i in [2, 3] if start <= i] + candidates_1 + candidates_2 return candidates if __name__ == '__main__': threads = 1 min_p, max_p = 2 ** 32, 2 ** 32 + 1_000_000 rep, num = 1, 1 f_name = 'pyfilename' times = timeit.repeat(setup=f"from {f_name} import primes_01; start={min_p}; end={max_p}", stmt="primes_01(start, end)", repeat=rep, number=num) print(sum(times)/(rep*num)) # import time # start = time.time() # primes = primes_01(min_p, max_p) # print(f"Dur: {time.time()-start}\n{sum([len(primes[i]) for i in range(len(primes))])}")
编辑补充:
上述代码耗时0.148s,而使用分段埃氏筛的Pyprimesieve处理相同区间(2^32到2^32+1,000,000)耗时21.9s,后者时间复杂度表现更差,对应代码如下:
import timeit min_p, max_p = 2**32, 2**32 + 1_000_000 rep, num = 1, 1 times = timeit.repeat(setup=f"import pyprimesieve; start={min_p}; end={max_p}", stmt="pyprimesieve.primes(start, end)", repeat=rep, number=num) print(sum(times)/(rep*num))
问题解答
一、矩阵并行化在素性检测中的应用
矩阵并行化可用于加速素性检测,但并非直接对单个素数检测做矩阵运算,而是针对批量候选数的并行调度:
- 批量候选数的并行映射:将候选数列表拆分为多个子块,借助NumPy向量化、CuPy GPU加速等框架,对每个子块的候选数同时执行素性检测逻辑。不过
gmpy2.is_prime本身是C实现的单线程函数,Python层面的矩阵并行框架可能无法完全释放硬件性能,需结合底层并行指令集或GPU加速库。 - Miller-Rabin测试的并行化:Miller-Rabin算法的多轮基测试可并行执行,可通过矩阵式任务调度,同时处理多个候选数的多轮基测试,本质是把独立测试任务批量并行化。
注意:矩阵并行化更适合大规模批量候选数场景,当区间仅1e6个数时,额外调度开销可能抵消并行收益,需根据实际规模权衡。
二、其他加速方法
1. 优化候选数筛选逻辑
- 扩展小素数过滤:在
6k±1的基础上,提前排除5、7、11等更多小素数的倍数,用位掩码或预计算余数表快速判断,减少需调用gmpy2.is_prime的候选数数量。 - 用生成器代替列表推导:避免一次性生成所有候选数占用内存,边生成边检测,适配更大区间的处理。
2. 并行化策略优化
- 多进程替代多线程:Python的GIL会限制CPU密集型任务的多线程效率,改用
multiprocessing或concurrent.futures.ProcessPoolExecutor,将候选数区间拆分为多个子区间,每个进程独立处理一个子区间,充分利用多核CPU。 - 控制任务粒度:拆分区间时避免粒度过小(如每个进程仅处理几个数),减少进程间通信开销;也不要粒度太大,确保每个CPU核心都有任务可执行。
3. 高效素性检测算法选型
- 指定Miller-Rabin测试基:对于
2^64以内的数,仅需测试基[2, 325, 9375, 28178, 450775, 9780504, 1795265022]即可保证确定性,比默认测试更快,可通过gmpy2.is_prime(n, reps=...)指定这些基。 - 概率测试+确定性验证结合:先对批量候选数做1-2轮快速概率性Miller-Rabin测试,过滤掉大部分合数,再对剩余数做确定性测试,减少总计算量。
4. 底层代码优化
- 编译型语言/C扩展:将核心素性检测逻辑用C/C++实现后通过Python调用,或用Cython、Numba对Python代码编译优化,降低Python解释器开销。
- 启用CPU指令集:确保编译时开启AVX、SSE等SIMD指令集,对批量候选数的模运算等操作做向量化加速,
gmpy2本身可能已支持,但需确认编译优化是否开启。
5. 精简冗余操作
- 提前判断
start与3的大小:若start > 3,可直接跳过2和3的判断逻辑,减少分支。 - 用数学公式替代循环:计算区间内第一个
6k+1和6k+5的数时,可直接用last_six_divisor_1 = ((start + 5) // 6) * 6 + 1代替循环迭代,提升效率。
内容的提问来源于stack exchange,提问作者JoshW
相关产品推荐
相关产品推荐

