了解快速素数发生器

时间:2017-08-18 07:55:18

标签: python primes

def sieve_for_primes_to(n):
    size = n//2
    sieve = [1]*size
    limit = int(n**0.5)
    for i in range(1,limit):
        if sieve[i]:
            val = 2*i+1
            tmp = ((size-1) - i)//val
            sieve[i+val::val] = [0]*tmp
    return sieve
print [2] + [i*2+1 for i, v in enumerate(sieve_for_primes_to(10000000)) 
if v and i>0]

有人可以描述一下这段代码是如何运作的吗?

2 个答案:

答案 0 :(得分:1)

这称为Sieve of Eratosthenes,维基页面可以很好地描述它。

它的要点是这样的:

您选择从2开始向上的数字,然后您:

  1. 将选中的数字标记为素数。

  2. 从您的设置中删除该数字的所有倍数,以防止将来选择它们。

  3. 见1)

答案 1 :(得分:0)

这实际上是Eratosthenes的压缩 Sieve,它不处理偶数,而只存储和检查奇数。这就是为什么从筛选索引到2 * i + 1的数字的原因。这减少了储存筛子所需空间的一半。

它不会选择从2 开始的数字,而是在之后将2添加到主要列表的前面。它从3(2 * 1 + 1)开始,然后上升到平方根。它不是使用循环来标记复合材料,而是巧妙地利用了Python的切片功能:

sieve[i + val::val] = [0] * tmp

将倍数设置为0(False)。但是,这些线似乎错误地超出了目标:

limit = int(n**0.5)
for i in range(1,limit):

由于limit是未压缩的值,未表示为索引,如下所示:

limit = int(n ** 0.5) // 2 + 1

这个(已更正的?)代码的注释版本:

def sieve_for_primes_to(n):
    size = n // 2  # allocate storage for odd numbers up to n
    sieve = [True] * size  # mark all odd numbers as prime to start
    limit = int(n ** 0.5) // 2 + 1  # int(sqrt(n)) as an index

    for index in range(1, limit):  # ie. number in range(3, int(sqrt(n)))
        if sieve[index]:  # if still marked as prime
            number = 2 * index + 1  # index to number conversion (1 -> 3)
            multiples = ((size - 1) - index) // number  # how many multiples from here to end of sieve
            sieve[index + number::number] = [False] * multiples  # mark odd composites/multiples False

    return sieve

print [2] + [2 * index + 1 for index, boolean in enumerate(sieve_for_primes_to(10000000)) if boolean and index > 0]

可以在这里进行另一项优化:

sieve[index + number::number] = [False] * multiples

这开始在当前素数的下一个奇数倍处标记复合倍数。但这意味着5开始标记为15,已经标记为复合3.我们实际上可以开始在当前素数的平方上标记复合倍数.25。对于OP,我们如何修改此压缩表示代码以合并此优化?