【问题标题】:Prime numbers with python [duplicate]python的素数[重复]
【发布时间】:2016-04-28 11:47:11
【问题描述】:

我对编程还很陌生,我决定做一些练习来提高我的能力。我坚持做一个练习:“找出所有低于 200 万的素数之和。”我的代码太慢了。

最初,我尝试将其作为一个普通的素数问题来解决,结果是这样的:

sum = 2 + 3
for i in range (5, 2000000, 2):
    for j in range (3, i, 2):
        if i%j == 0:
            break
    else:
        sum += i
print(sum)

这样,所有偶数都会被排除在循环之外。但这并没有解决我的问题。这里的量级真的很大。

所以我试图了解这段代码发生了什么。我在循环中有一个循环,循环内的循环运行外部循环时间的索引(不完全是因为列表不是从 0 开始的),对吧?因此,当我尝试查找 20 以下的素数时,它运行外部循环 8 次,但内部循环运行 60 次(我不知道这个数学是否正确,正如我所说,我对编程非常了解)。但是当我将它与 2,000,000 一起使用时,我总共运行了大约 999,993,000,012 次内部循环,这太疯狂了。

我的朋友告诉我埃拉托色尼筛法,我尝试创建一个新代码:

list = [2]
list.extend(range(3, 2000000, 2))
for i in list:
    for j in list:
        if j%i == 0 and j > i:
            list.remove(j)
print(sum(list))

这就是我尝试模拟筛子所取得的成果(忽略偶数有帮助)。它要快得多(使用其他代码,找到 200,000 以下的素数需要很长时间,而使用这个新代码我可以做到),但在合理的时间内计算 2,000,000,000 是不够的。自从我开始编写代码以来,代码一直在后台运行,但仍然没有。这玩意不知道循环了多少次,现在想起来也累了。

我来这里寻求帮助。为什么这么慢?我应该学习/阅读/做什么来改进我的代码?还有比这个筛子更有效的方法吗?感谢您的宝贵时间。

【问题讨论】:

标签: python performance loops primes


【解决方案1】:

因为list.remove 是一个O(n) 操作,而您经常这样做。而且你不是在表演真正的筛子,只是变相的试除;你仍在做你在原始代码中所做的所有剩余测试。

埃拉托色尼筛法通常使用一组标志来实现;在最简单的形式中,每个索引对应于相同的数字,并且对于除01 之外的所有索引,初始值都是True。您继续迭代,当您找到一个True 值时,您将所有是它的倍数的索引设置为False。这意味着工作是顺序加法,而不是乘法,而不是除法(成本要高得多。

【讨论】:

  • 你能再解释一下吗?
  • 好的,第一个筛分案例。当您迭代时,您会发现索引 2 为真(素数)。此时,您知道所有 2 的倍数都不是定义的素数。因此,您可以从2 * 2 循环到2000000,而不是任何乘法或除法,步进2(例如使用range(p * p, len(flags), p)),并将标志列表中的条目设置为False。不需要大的内存移动(甚至不检查不是倍数的标志),不会发生乘法或余数测试(xrange/range 加法更快)。
  • 3 是一样的;该标志仍然为真,因此它是主要标志,并且您将设置从3 * 32000000 的所有标志,逐步通过3False(实际上,作为一种优化,除了2,您可以通过2 * p,或63,因为偶数值绝对不是素数)。
  • @settifoglio:如果你想要例子,Rosetta code has multiple implementations in Python。它们不是超优化的(通过巧妙地使用bytearray,基于“数组”的版本可以在速度和性能上大大提高),但它们在算法上明显优于你所拥有的,并且应该绰绰有余筛选前 2M 个素数(使用超优化代码,您可以轻松地将工作量减少 10 倍或更多,但在这里减少一两秒并不重要)。
  • 谢谢您,先生。我一定会尝试你的建议和罗塞塔。你说的很有道理,非常感谢。
猜你喜欢
  • 2018-11-15
  • 2014-04-08
  • 2013-10-23
  • 2019-06-28
  • 2014-07-13
  • 1970-01-01
  • 1970-01-01
  • 2014-08-02
  • 2021-12-30
相关资源
最近更新 更多