【问题标题】:Why is my Sieve of Eratosthenes running so slowly?为什么我的埃拉托色尼筛运行得这么慢?
【发布时间】:2017-10-09 22:54:12
【问题描述】:

我正在用 Python 编写一个用于 Eratosthenes 筛子的素数程序。虽然它似乎工作,但它非常缓慢。如何加快速度?

primes = []
upperLimit = 1000

for x in range(2,upperLimit):
  primes.append(x)
for y in range(0,int(len(primes)**0.5)):
  remove = []
  for j in range(primes[y]**2,upperLimit,primes[y]):
    remove.append(j)
  for i in remove:
    if i in primes:
      primes.remove(i)

print(primes)

更新: 感谢答案的帮助,我使用布尔值而不是数字重写了代码。低于 100000 的列表现在运行时间不到 6 秒。

i = 2
limit = 100000
primes = [True] * limit
primes[0] = False
while i < limit**0.5:
    if primes[i-1]:
        for x in range(i * 2, limit + 1,i):
            primes[x-1] = False
    i += 1
count = 1
total = []
for i in primes:
    if i:
        total.append(count)
    count += 1
print(total)

【问题讨论】:

  • 慢有多慢,您使用的是什么系统/Python?您还应该问自己是否创建了太多列表,从而创建了大量不需要创建的内存。
  • 罪魁祸首是primes.remove,这是一个O(N)操作
  • 在 20000 的限制下运行大约需要 30 秒,当我尝试超过 1000000 的值时,我在大约 10 分钟后才停止

标签: python algorithm primes sieve-of-eratosthenes


【解决方案1】:

我认为您的代码中主要的低效率是您维护的素数list。尽管可能并不明显,但调用primes.remove 是一项非常昂贵的操作。它需要遍历list 以尝试找到您要删除的值,然后需要通过将所有元素移到您要查找的元素之后来修改list

例如

l = [0, 1, 2, 3, 4]
l.remove(5)  # This has to look at all the elements in l, since 6 isn't there
l.remove(0)  # This finds 1 quickly but then has to move every element to the left

一种更传统的埃拉托色尼筛法是使用一个包含您正在考虑的所有数字的数组(Python 中的list),其中每个元素都是一个布尔值,指示该数字是否可以是素数。

模仿上面的例子:

l = [True, True, True, True, True]
l[0] = False  # Just goes straight to that element and changes its value

以下是如何编写该代码的示例:

primes = [True] * 100000

# We already know 2 is the first prime
primes[0] = False
primes[1] = False

# Fine to stop at sqrt(len(primes)) here as you're already doing    
for i in range(2, len(primes)):
    if primes[i]:
        for j in range(i**2, len(primes), i):
            primes[j] = False

print([n for n, is_prime in enumerate(primes) if is_prime])

您会发现这要快得多,因为索引到 list 并以这种方式更改值非常有效。

【讨论】:

  • if 语句有什么作用?还有,最后一行代码(打印函数)做了什么?
  • if 语句只是跳过已经被证明不是素数的数字。打印语句有点像for i in range(len(primes)): if primes[i]: print(i)。它会打印出list 中仍标记为True(可能是素数)的数字。
【解决方案2】:

这很慢,因为您执行的许多操作比需要的频率高得多。合数 N 的寿命看起来像这样:

  • N 附加到 素数
  • 对于每个 small 数字 I (
  • I 的所有 倍数附加到 remove
  • 对于删除中的每个
    • 如果仍处于质数,请将其删除。
  • 每个数字都有很多“接触”。这也是很多需要考虑的数字。

    试试这个:

    • 制作一个布尔值列表(全部True),每个可能的素数都有一个。
    • 虽然最低值 I 标记为 True
    • 清除(转FalseI的所有大倍数
    • 注意:不用检查值是否已经是False

    此时,您的质数正是那些仍标记为 True

    的值

    【讨论】:

      猜你喜欢
      • 2015-08-12
      • 2011-12-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多