分割如何提高埃拉托斯尼筛的运行时间

How does segmentation improve the running time of Sieve of Eratosthenes?

本文关键字:运行时间 何提高 埃拉托 分割      更新时间:2023-10-16

我发现了一个Eratosthenes筛的分段实现,它的运行速度有望比传统版本快很多倍。有人能解释一下分段是如何提高运行时间的吗?注意,我想在[1,b]中找到素数。

它在这个想法上起作用:(用于寻找直到10^9的素数)

  • 我们首先生成sqrt(10^9)以下的筛选素数,这些素数是划掉倍数所必需的。然后我们开始将第一素数2的倍数划掉,直到我们达到2>=的倍数segment_size,如果发生这种情况,我们使用(multiple-segment_size)计算下一个段中该倍数的索引,并将其存储在一个单独的数组中(next[])。然后,我们使用相同的程序划掉下一个筛选素数的倍数。一旦我们划掉了第一段中所有筛选素数的倍数,我们就在筛数组上迭代,并打印(或计数)素数。

  • 为了筛选下一个片段,我们重置了筛选数组,并将较低的偏移量增加segment_size。然后我们再次开始划掉倍数,对于每个筛选素数,我们从下一个数组中检索筛选指数,然后开始划掉从那里开始的倍数…

分段筛执行与常规筛相同的所有操作,因此big-O时间复杂性不变。区别在于记忆的使用。如果筛子足够小,可以放在记忆中,那就没有区别了。随着筛网尺寸的增加,参考位置成为一个因素,因此筛分过程减慢。在极端情况下,如果筛子不适合内存,必须分页到磁盘,筛选过程将变得非常缓慢。分段筛选使内存大小保持不变,而且可能很小,因此对筛选的所有访问都是本地的,因此速度很快。

即使筛子完全适合RAM,访问的局部性仍然会产生很大的影响。只有Eratosthenes的几率的C++实现几乎需要半分钟来筛选前2^32个数字;同样的实现在256 KB的小段(2^21位,表示2^22个数字)中初始化相同的筛子,在我的老化Nehalem上只需8.5秒,它有256 KB的L2缓存。

在小缓存友好段中筛选的速度在范围的较高区域减小,因为无论段大小,筛选每次都必须迭代所有因子,直到sqrt(n)。这在接近2^64的段中最为显著,其中小因子包括203280221素数(即完整的32位筛选)。

不过,分段操作仍然胜过全面筛选。你可以筛选接近2^64的片段,每秒数百万个素数,在较低的区域每秒数千万个素数。这是在计算素数,而不是原始数矿石。即使你有很多记忆,超过2^32左右的全筛也不实用。