如何在此质数筛的for循环中花费更少的时间?

时间:2019-07-12 22:59:51

标签: python performance for-loop sieve-of-eratosthenes

下面所示的Eratosthenes筛网的实现效率很低。我对找到最有效率的人不感兴趣;我只是想使自己已经变得更好的东西。

我知道这样做的方式大不相同;我只是从头顶实现了筛子,我的问题是我想知道我能做些什么,以使这种特定实现中的特定部分更加有效。

def sieve(x):
    l = []
    for i in range(x + 1):
        l.append(i)
    l.remove(0)
    l.remove(1)
    clone = l[:]
    test = 0
    for i in l:
        while test < len(clone):
            checker = clone[test]
            if checker % i == 0 and i != checker and i < checker:
                clone.remove(checker)
            test += 1
        test = 0
    return clone
print(sieve(24))

如果将上面的代码粘贴到http://pythontutor.com/visualize.html#mode=edit中并逐步执行,您会发现在第200步之前已经找到了所有素数;它通常会浪费时间在外部for循环中。这样的操作问题是:在这个特定的实现中,如何减少它在for循环中花费的时间?我为提高效率所做的最大努力就是用对clone[test]的单个分配替换checker的四个实例(好吧,无论如何,每个循环一个分配)。

1 个答案:

答案 0 :(得分:1)

首先,标记元素已删除(在已知位置进行单个分配)比使用list.remove(需要在列表中进行搜索并将每个元素移至一个被删除)。然后,您可以在最后一遍将它们全部过滤掉。因此,就像您已经在做的一样,从0到x的索引开始是素数:

l = [True] * (x + 1)

然后将某些标记为非素数

l[0] = False
l[1] = False

然后,您可以简化内部循环,以便每次都向前移动固定数量的元素:

for i in range(2, x + 1):
    if not l[i]:
        continue

    for j in range(i * i, x + 1, i):
        l[j] = False

i * i开始,因为当循环命中i时,已经覆盖了s * i形式的s的任何较小倍数。这也具有不涉及除法运算的优点。

使用表示给定索引是否为素数的布尔值列表构成素数列表,然后:

return [i for i in range(x + 1) if l[i]]

这一切都没有减少外部循环的迭代次数,但是它应该已经快得多了,并且使用简化版本,也许更容易了解如何在i * i停止时停止大于列表,也跳过对每个偶数的检查。