Python代码优化

时间:2014-01-07 21:15:45

标签: python performance python-3.x

最近我发现了一个谜题,要求我列出一个数字下面的所有循环素数。 在这种情况下,循环意味着如果我们旋转数字它仍然是素数: 例如。 1193是素数 1931年是素数 9311是素数 3119是素数

这是我原来写的代码:

a=[]
upto=1000000

for x in range(upto):
    a.append([x,0])

print('generated table')

a[1][1]=1
a[0][1]=1

for n in range(2,int(math.sqrt(upto))):
    for k in range(2,(int(upto/n)+2)):
        try:
            a[n*k][1]=1
        except IndexError:
            pass
print('sive complete')

p=[]
for e in a:
    if (e[1]==0):
        p.append(e[0])
print('primes generated')

s=[]
for e in p:
    pr=True
    w=str(e)
    if all(c not in w for c in ['2','4','6','8','5','0']):
        for x in (w[i:]+w[:i] for i in range(len(w))):
            if int(x) not in p:
                pr=False
        if pr==True:
            s.append(e)
            print('found',e)
print(s)

这很慢! (大约12s)我知道,素数不是很完美,但最后一点是最慢的。我知道upto = 10e6的这个过程可以在一秒钟内完成,所以在经过一些研究后我删除了任何字符串操作以支持这个函数:

def rotate(n):
    prev=[]
    for l in range(6,0,-1):
        if(n<10**l):
            length=l
    while(n not in prev):
        prev.append(n)
        n=(n // 10) + (n % 10) * 10**(length-1)
        yield n

我还删除了5,0,2,4,6,8测试,因为我不知道如何实现它。结果?它运行得更慢! (超过十分钟,我想5,0,2,4,6,8测试是一个好主意)

我尝试使用time.time()但是我找不到任何非常低效的东西(在第一个代码中)。如何改进此代码?我目前正在使用哪些不良做法?

2 个答案:

答案 0 :(得分:2)

以下是一些优化代码:

import math

upto = 1000000

a = [True] * upto
p = []

for n in xrange(2,upto):
    if a[n]:
        p.append(n)
        for k in xrange(2,(upto+n-1)//n):
            a[k*n] = False

print('primes generated')

s = []
p = set(p)
for e in p:
    pr=True
    w=str(e)
    if all(c not in w for c in ['2','4','6','8','5','0']):
        for x in (w[i:]+w[:i] for i in range(len(w))):
            if int(x) not in p:
                pr=False
                break
        if pr:
            s.append(e)

print(s)

最重要的优化:

  1. 简化了筛选代码
  2. 将素数列表转换为集合。这使得测试x in p成为logaritmic而不是linear
  3. 在找到非主要轮换时添加了一个break语句
  4. 添加了更干净(但等效)的代码:

    import math
    
    upto=1000000
    
    sieve = [True] * upto
    primes = set()
    
    for n in xrange(2,upto):
        if sieve[n]:
            primes.add(n)
            for k in xrange(2,(upto+n-1)//n):
                sieve[k*n] = False
    
    def good(e):
        w = str(e)
        for c in w:
            if c not in '1379':
                return False
        for i in xrange(1,len(w)):
            x = int(w[i:]+w[:i])
            if x not in primes:
                return False
        return True
    
    print filter(good,primes)
    

答案 1 :(得分:1)

您可以通过进行集合比较来减少第一次测试所需的时间,而不是每次都进行完整的迭代:

flags = set('246850')
if not set(str(e)).intersection(flags):
    # etc...

这不仅可以按对数方式进行缩放,还可以让您在此步骤中选择另外两个因子。您甚至可以通过将其转换为生成器来进一步加快速度并使其更加优雅,然后您可以使用它来进行最终检查:

flags = set('246850')
primes = set(p)
easy_checks = (str(prime) for prime in primes if not set(str(prime)).intersection(flags))

最后你可以重写最后一点来摆脱所有追加和诸如此类的东西,这往往是超级慢如下:

test = lambda number: any((int(number[i:]+number[:i]) in primes for i in xrange(len(number))))
final = [number for number in easy_checks if test(number)]