加快字符串拆分和连接

时间:2011-01-25 12:03:07

标签: python primes

我正在尝试解决Project Euler's problem #35

  

这个数字197被称为循环素数,因为数字的所有旋转:197,971和719本身都是素数。

     

在一百万以下有多少个圆形素数?

这是我的解决方案:

import numpy as np

def problem(n=100):

    circulars = np.array([], np.int32)

    p = np.array(sieveOfAtkin(n), np.int32)
    for prime in p:
        prime_str = str(prime)
        is_circular = True
        for i in xrange(len(prime_str)):
            m = int(prime_str[i:]+prime_str[:i])
            if not m in p:
                is_circular = False

        if is_circular:
            circulars = np.append(circulars, [prime])

    return len(circulars)

不幸的是for循环非常慢!我有什么想法可以加快速度吗? 我怀疑字符串连接是瓶颈,但我不完全确定! :)


有什么想法吗? :)

1 个答案:

答案 0 :(得分:8)

  1. 使用集合进行成员资格测试而不是数组。哈希查找将是O(1)而不是O(n)。这是最大的瓶颈。

  2. 一旦看到它不是循环素数而不是尝试其他旋转,就会突破循环。这是另一个瓶颈。


  3. 在这里,我已将圆度测试分离为一个函数,以允许使用列表推导构建列表。将它放在一个函数中,只要我们知道它不是循环的,就让它返回False。另一种选择是在for循环和break中进行,当我们知道它不是循环时。然后附加到循环的else子句中的列表中。一般来说,列表组合比在循环中附加更快。这可能不是这种情况,因为它确实增加了函数调用开销。如果你真的关心速度,那么分析这两个选项是值得的。

    primes = set(primes_to_one_million_however_you_want_to_get_them)
    
    def is_circular(prime, primes=primes):
       prime_str = str(prime)
       # With thanks to Sven Marnach's comments
       return all(int(prime_str[i:]+prime_str[:i]) in primes 
                  for i in xrange(len(prime_str)))
    
    
    circular_primes = [p for p in primes if is_circular(p)]
    

    我还使用了将全局作为默认参数传递给is_circular函数的技巧。这意味着它可以作为局部变量在函数内访问,而不是更快的全局变量。

    这是使用循环上的else子句对其进行编码的一种方法,以摆脱那个丑陋的旗帜并提高效率。

    circular = []
    for p in primes:
       prime_str = str(prime)
       for i in xrange(len(prime_str)):
           if int(prime_str[i:]+prime_str[:i]) not in primes:
                break
       else:
           circular.append(p)