下面所示的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
的四个实例(好吧,无论如何,每个循环一个分配)。
答案 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
停止时停止大于列表,也跳过对每个偶数的检查。