如何提升Parallel IntStream生成大量素数的效率与速度?
优化IntStream生成百万素数的提速方案
嘿,我完全懂你的困扰——用parallelStream生成100万素数花了7秒多,确实离“数秒内生成数百万”的目标差得远。咱们从算法选型到并行策略一步步优化,绝对能把速度提上来:
1. 换掉试除法,改用埃拉托斯特尼筛法(核心优化)
你当前应该是用“逐个判断每个数是否为素数”的试除法,这种方法的时间复杂度是O(n√n),数据量上去后会非常慢。而埃氏筛法的时间复杂度是O(n log log n),批量处理素数的效率碾压试除法,尤其适合生成连续范围内的素数。
用BitSet实现的埃氏筛法内存占用极低,生成100万以内的素数只需要约125KB内存,而且生成速度极快:
public static IntStream generatePrimesWithSieve(int max) { if (max < 2) return IntStream.empty(); // 初始化BitSet,默认所有位为false,我们标记素数为true BitSet sieve = new BitSet(max + 1); sieve.set(2, max + 1); // 先把2到max的所有数标记为素数候选 // 从2开始,遍历到平方根 for (int i = 2; i * i <= max; i++) { if (sieve.get(i)) { // 如果i是素数,清除它的所有倍数 sieve.clear(i * i, max + 1, i); // 从i²开始,步长i清除 } } // 把BitSet中为true的位转成IntStream return sieve.stream(); }
这个方法生成100万以内的素数大概只需要几十毫秒,完全符合你的需求。
2. 若必须用试除法,优化判断逻辑
如果因为某些场景不能用筛法(比如生成非连续的素数),那可以把试除法的判断逻辑榨干性能:
- 提前排除偶数:除了2,所有偶数都不是素数,所以生成流时直接跳过偶数,减少一半的判断量:
IntStream primes = IntStream.concat( IntStream.of(2), IntStream.range(3, max).filter(n -> n % 2 != 0) ).filter(YourClass::isPrime); - 试除范围缩小到平方根:判断素数时,只需要试除到
Math.sqrt(n),而不是n的一半。 - 只试除已有的素数:维护一个小素数列表,用这些素数去试除,而不是所有奇数。
3. 优化并行流的执行效率
如果用并行流,别依赖默认的ForkJoinPool:
- 自定义线程池:根据你的CPU核心数调整线程数,避免线程过多导致上下文切换开销:
ForkJoinPool customPool = new ForkJoinPool(Runtime.getRuntime().availableProcessors()); try { customPool.submit(() -> { // 在这里执行你的并行流逻辑 IntStream.range(...) .parallel() .filter(...) .count(); }).get(); } catch (Exception e) { e.printStackTrace(); } finally { customPool.shutdown(); } - 避免并行流中的线程不安全操作:如果你的素数判断逻辑有共享状态(比如缓存素数),一定要用线程安全的容器,或者让每个线程维护自己的局部缓存。
4. 生成超大范围素数:用分段筛法
如果需要生成数百万甚至上亿的素数,普通埃氏筛法会因为内存不足受限,这时候可以用分段筛法(Segmented Sieve):
- 先生成小范围的素数(比如√max以内的素数),作为筛子的基础。
- 把目标范围分成多个小段,每个段用基础素数去筛,并行处理每个段。
这种方法内存占用低,还能充分利用多核CPU,生成上亿素数也能在几秒内完成。
内容的提问来源于stack exchange,提问作者iViscosity
相关产品推荐
相关产品推荐

