【问题标题】:Speeding up string splitting and concatenation加速字符串拆分和连接
【发布时间】:2011-01-25 12:03:07
【问题描述】:

我正在尝试解决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 循环非常慢!有什么想法可以加快速度吗? 我怀疑字符串连接是瓶颈,但我不完全确定! :)


有什么想法吗? :)

【问题讨论】:

  • 你为什么要使用字符串?
  • 不要为 circulars 使用 NumPy 数组——它具有固定大小,需要在 每次 调用 numpy.append() 时重新分配。 Python 列表是这里更好的选择。 (删除 numpy 标签,因为问题和当前答案都与 numpy 无关。)

标签: python primes


【解决方案1】:
  1. 对成员资格测试使用集合而不是数组。哈希查找将是 O(1) 而不是 O(n)。这是最大的瓶颈。

  2. 一旦你发现它不是一个圆形素数,就立即跳出循环,而不是尝试其他旋转。这是另一个瓶颈。


在这里,我将循环测试隔离到一个函数中,以允许使用列表理解构建列表。将它放在一个函数中,一旦我们知道它不是循环的,就让它返回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)

【讨论】:

  • @RadiantHex。阅读哈希表。答案是集合是使用哈希表实现的。这允许 O(1) 成员资格测试。对于列表或数组,成员资格测试涉及遍历 每个 元素,直到找到匹配项。在找不到匹配项的最坏情况下,必须查看每个元素,因此它是 O(n)。
  • @aaron:太棒了!非常感谢!我今天确实学到了一些有用的东西:)
  • is_circular()的最后四行可以改写为return all(int(prime_str[i:]+prime_str[:i]) in primes for i in xrange(len(prime_str)))。这应该会更快。
  • @Sven 很好。请参阅我的编辑。这样做,可以让您完全摆脱函数调用开销。
  • 哦,还有一句话——xrange 可能从 1 开始,因为我们已经知道 p 是素数。
猜你喜欢
  • 2013-01-03
  • 2011-10-07
  • 1970-01-01
  • 2014-10-27
  • 1970-01-01
  • 1970-01-01
  • 2017-08-16
  • 1970-01-01
  • 2018-04-13
相关资源
最近更新 更多