【问题标题】:Python prime number code running slowPython素数代码运行缓慢
【发布时间】:2011-03-06 01:42:28
【问题描述】:

我正在尝试解决这里提到的问题:https://www.spoj.pl/problems/PRIME1/

我也在下面给出描述。

Peter 想为他的密码系统生成一些素数。帮助他!你的任务是生成两个给定数字之间的所有素数!

输入

输入以单行中的测试用例数量 t (t

输出

对于每个测试用例,打印所有质数 p 使得 m

我的代码如下。我认为列表中的删除方法非常慢。

import sys
import math

num = int(sys.stdin.readline());
indices = []
maxrange = 2
while(num > 0):
    a,b = sys.stdin.readline().split(" ");
    a = int(a)
    b = int(b)
    if(a < 2):
        a = 2
    indices.append((a,b))
    if(b > maxrange):
        maxrange= b          
    num = num - 1

val = int(math.sqrt(maxrange)+1)
val2 = int(math.sqrt(val)+1)
checks = range(2,val2)

for i in range(2,val2):
    for j in checks:
        if(i!= j and j%i == 0):
            checks.remove(j)

primes = range(2,val)
for i in checks:
    for j in primes:
        if(i != j and j%i == 0):
            primes.remove(j)

primes2 = range(2,maxrange)
for i in primes:
    for j in primes2:
        if(j != i and j%i == 0):
            primes2.remove(j)

for (a,b) in indices:
    for p in primes2:
        if(a<= p and b >= p):
            print p
        if(p > b):
            break
    print

我认为 python list remove 很慢。我的代码是正确的,但我超出了时间限制。有人可以帮我改进这段代码吗?

【问题讨论】:

  • 你有profiled吗?
  • 我还没有这样做..让我来做吧。

标签: python algorithm data-structures primes


【解决方案1】:

素数测试功能将表现最佳。 Miller-Rabin wikipedia page上有伪代码

【讨论】:

  • 这不是概率答案吗?十万,我认为这需要相当长的时间。
  • 如果您点击维基百科页面的链接,您会看到有一个确定性、有效的变体适用于您感兴趣的数字范围。您不需要检查所有100K 个数字,例如,您只需要考虑 1 或 5 模 6 的数字。
  • 因此,您的确定性变体必须测试 1/3 的数字作为天真的 100K 检查。因此,该变体最多需要 3 倍于幼稚 100K 检查的运行时间才能更快。好吧,你可以优化简单的 100K 检查,只在 2 之后做奇数。所以比率是 1/3N 到 1/2N(如果你折扣任何以 5 结尾的东西,当 > 5 时,你会得到 1/ 3N 到 2/5N - 所以你的速度优势会减少到 20% 左右)。
  • @typo 你确定你读过这个问题吗?它是生成给定 m 和 n 之间的素数,而不是生成所有低于 100K 的素数。
  • 是的,我已经阅读了这个问题。给你两个数字(m
【解决方案2】:

与其删除不是素数的元素,不如用一些标记值替换它,比如-1None?然后在打印时,只打印不是哨兵的值。

使用长度为(n-m)的列表,然后编号i的索引为x[m+i]

【讨论】:

  • 如何替换它..我在列表中没有索引
  • 我的错,我看你的代码太快了。我相信这是列表中的索引方法。但是,每次查找的列表长度都是线性的,因此它本身可能非常昂贵......但是,知道下限是多少,您可以有一个列表,其中 I 位于 @987654327 @,只需使用索引查找
  • 这确实是要走的路,即使您必须更改算法以便拥有索引。
【解决方案3】:

remove() 从整体上看并不慢​​,只是代码称它为很多。

正如 dappawit 所建议的,与其修改列表,不如更改列表中的值,这样您就知道它不是要使用的有效数字。

我还看到,当您生成素数集时,您使用range(2,maxrange) 这没问题,但如果下限远大于 2,则效率不高。您将浪费计算时间来生成素数甚至与问题空间无关。如果不出意外,请跟踪 minrange 和 maxrange。

原始代码的一个错误是您使用了range(2,maxrange)。这意味着maxrange 不在考虑的数字列表中。尝试将 3 5 作为 a 和 b 的输入来查看错误。

range(2,maxrange+1) 解决了这个问题。

代码中的另一个错误是您修改了原始序列:

来自Python docs - for-statement

在循环中修改被迭代的序列是不安全的(这只会发生在可变序列类型上,例如列表)。如果您需要修改您正在迭代的列表(例如,复制所选项目),您必须迭代一个副本。切片符号使这变得特别方便:

我的 Python 技能还很初级,但这似乎行得通:

旧:

primes2 = range(2,maxrange)
for i in primes:
     for j in primes2:
         if(j != i and j%i == 0):
             primes2.remove(j)

for (a,b) in indices:
    for p in primes2:
        if(a<= p and b >= p):

新:

primes2 = array.array('L', range(minrange,maxrange+1))
for i in primes:
    for j in primes2:
        if(j != i and j%i == 0):
            primes2[j-minrange] = 0

for (a,b) in indices:
    for p in primes2:
        if (p != 0):
            if(a<= p and b >= p):

您也可以跳过生成素数集并直接测试数字,如果您必须生成素数的数字集不重叠(没有工作重复),这将起作用。 enter link description here

【讨论】:

    【解决方案4】:

    这是Miller–Rabin primality test 的确定性变体,用于 Python 中的小奇数:

    from math import log
    
    def isprime(n):
        assert 1 < n < 4759123141 and n % 2 != 0, n
    
        # (n-1) == 2**s * d
        s = 0
        d = n-1
        while d & 1 == 0:
            s += 1
            d >>= 1
        assert d % 2 != 0 and (n-1) == d*2**s
    
        for a in [2, 7, 61]:
            if not 2 <= a <= min(n-1, int(2*log(n)**2)):
                break
            if (pow(a, d, n) != 1 and
                all(pow(a, d*2**r, n) != (n-1) for r in xrange(s))):
                return False
        return True
    

    代码意图是一个可执行的伪代码。

    【讨论】:

      猜你喜欢
      • 2018-09-16
      • 2021-11-05
      • 2015-03-11
      • 1970-01-01
      • 1970-01-01
      • 2021-05-22
      • 2018-10-17
      • 2016-03-28
      • 1970-01-01
      相关资源
      最近更新 更多