我正在尝试解决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循环非常慢!我有什么想法可以加快速度吗? 我怀疑字符串连接是瓶颈,但我不完全确定! :)
有什么想法吗? :)
答案 0 :(得分:8)
使用集合进行成员资格测试而不是数组。哈希查找将是O(1)而不是O(n)。这是最大的瓶颈。
一旦看到它不是循环素数而不是尝试其他旋转,就会突破循环。这是另一个瓶颈。
在这里,我已将圆度测试分离为一个函数,以允许使用列表推导构建列表。将它放在一个函数中,只要我们知道它不是循环的,就让它返回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)