2011-03-06 213 views
0

Python的素数的代码我试图解决此处提到的问题:https://www.spoj.pl/problems/PRIME1/运行缓慢

我也给下面的描述。

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

输入

输入开始的测试用例在一行数吨(吨< = 10)。在每个接下来的t行中,有两个空格分隔的数字m和n(1 < = m < = n < = 1000000000,n-m < = 100000)。

输出

对于每个测试实例打印所有素数p使得m < = P < = n时,用空line.`分开每行一个数,测试用例

我的代码如下所示。我想列表中的删除方法非常缓慢。

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很慢。我的代码是正确的,但我超过了timelimit。有人可以帮我改进这个代码。

+3

你有[profiled](http://docs.python.org/library/profile.html)吗? – nmichaels 2011-03-06 01:55:13

+0

我还没有做到这一点..我做到了。 – Boolean 2011-03-06 02:00:16

回答

2

除了删除不是主要元素的元素,为什么不用一些标记值替换它,可能是-1None?然后打印时,只打印不是哨兵的值。

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

+0

如何替换它..我没有在列表中的索引 – Boolean 2011-03-06 02:00:58

+0

我的错误,我看你的代码太快。我相信这是列表中的索引方法。然而,每次查找的列表长度都是线性的,所以它本身可能相当昂贵......但是,知道下限是什么,可以在'lower_bound'找到'I'的列表+ I',并且只需使用索引查找 – dappawit 2011-03-06 02:08:42

+0

即使您必须更改算法以使您拥有索引,这也是真正的方法。 – hop 2011-03-06 13:44:01

1

remove()在事物的宏观方案中并不慢,只是代码将其称为LOT。

正如dappawit所建议的,不是修改列表,而是改变列表中的值,以便知道它不是一个有效的数字。

我也看到,当你生成一组素数时,你可以使用range(2,maxrange),这是可以的,但如果下限远大于2,则效率不高。你将浪费计算时间来生成素数,甚至与问题空间有关。如果没有其他,跟踪最小范围以及最大范围。

与原始代码的错误是您使用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

3

素数测试功能将表现最好。有一个在Miller-Rabin wikipedia page

+0

这不是一个概率答案?十万,我认为这将需要很长的时间。 – Boolean 2011-03-06 23:29:59

+0

如果您按照维基百科页面的链接,您会看到有一个确定性,高效的变体,适用于您感兴趣的数字范围。您不需要检查所有100K数字,例如您需要只考虑1或5模6的那些。 – 2011-03-07 07:29:29

+0

因此,您的确定性变体必须测试的数量是天真100K检查的1/3。因此,该变体需要至多是天真100K检查运行时间的3倍,速度更快。那么,你可以优化朴素的100K检查,并且只在2之后做奇数。那么比率是1/3N到1/2N(如果你打折以5结尾的任何东西,当大于5时,你会达到1/3N到2/5N - 所以你的速度优势会减少到20%左右)。 – 2011-03-07 19:39:49

1

伪这里是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 

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