这在任何方面都是寻找素数的最佳解决方案吗?我不是想在阳光下添加所有优化,但主要是好处吗?
def primesUpto(self, x):
primes = [2]
sieve = [2]
i = 3
while i <= x:
composite = False
j = 0
while j < len(sieve):
sieve[j] = sieve[j] - 1
if sieve[j] == 0:
composite = True
sieve[j] = primes[j]
j += 1
if not composite:
primes.append(i)
sieve.append(i*i-i)
i += 1
return primes
答案 0 :(得分:4)
这是非常低效的。 Tested on Ideone它运行在相同的empirical order of growth ~ n^2.2
(在所测量的几千个素数的测试范围内)作为着名的低效Turner's trial division sieve(在Haskell中)。
为什么呢?几个原因。测试中 第一次 ,没有早期救助:当您检测到它是一个复合时,您继续处理计数器数组sieve
。您必须,因为 第二个原因 :您计算差异通过递减每个计数器1步骤,0表示您当前的位置。这是原始筛选IMHO最忠实的表达,效率非常低:今天我们的CPU知道如何在O(1)时间内添加数字(如果这些数字属于某个范围,0 .. 2 ^ 32,或者0 .. 2 ^ 64,当然)。
此外,我们的计算机现在也有直接访问内存,并且计算了我们可以在随机访问数组中标记它的远端数。这是Eratosthenes在现代计算机上筛分效率的基础 - 直接计算和多重直接标记。
第三 ,也许是效率低下的最直接原因,是 过早 处理倍数:当你遇到5作为素数时,你将它的第一个倍数(尚未遇到),即25,马上添加到计数器数组sieve
(即当前点和倍数,i*i-i
)。那太早了。添加25必须 推迟 ,直到......好吧,直到我们在升序自然数中遇到25。开始过早地处理每个素数的倍数(在p
而不是p*p
)导致有太多的计数器需要维护 - O(n)
它们(其中n
是产生的素数),而不仅仅是O(π(sqrt(n log n))) = O(sqrt(n / log n))
。
推迟优化when applied on a similar "counting" sieve in Haskell带来了经验性的增长订单,从~ n^2.3 .. 2.6
n = 1000 .. 6000
素数增长到~ n^1.5
以上(显然巨大)速度提高)。当计数进一步被直接加法取代时,由此产生的经验增长的经验序列为~ n^1.2 .. 1.3
,产生高达一百万个素数(尽管很可能在较大范围内获得~ n^1.5
。) / p>