【问题标题】:How do I spend less time in the for loop in this prime number sieve?如何在这个素数筛的 for 循环中花费更少的时间?
【发布时间】:2019-07-12 22:59:51
【问题描述】:

下面显示的 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 进行了一次分配(好吧,无论如何,每个循环都有一次分配)。

【问题讨论】:

  • 如果代码有效并且您只需要改进建议,则应改为在 Code Review 上。但请注意,他们会要求您提供 main 以显示其运行方式。
  • 我可以要求搬家吗?我需要标记它吗?如果我这样做,我会失去我刚刚在这里获得的代表吗?
  • 我投票结束这个问题,因为它属于codereview.stackexchange.com

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


【解决方案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 开始,因为s * i 形式的i 的任何较小倍数在循环命中s 时已经被覆盖。这还具有不涉及除法运算的优点。

使用指示给定索引是否为素数的布尔值列表来制作素数列表,然后:

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

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

【讨论】:

    猜你喜欢
    • 2013-03-05
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-05
    • 1970-01-01
    相关资源
    最近更新 更多