import math
primes = []
def get_primes(lower, upper):
for num in range(lower, upper):
for i in range(2, num):
if num % i == 0:
break
else:
primes.append(num)
print get_primes(int(raw_input("Enter lower boundary")), int(raw_input("Enter upper boundary")))
print primes
for i in range(0, len(primes) - 1):
if primes[i+1] - primes[i] == 2:
print str(primes[i]), str(primes[i+1])
#1 楼
样式称为PEP 8的Python代码样式指南建议使用4个空格的缩进。
更多功能
您可以将逻辑拆分为更小的易于理解,测试和优化的逻辑部分(稍后再介绍)。
例如:
def is_prime(n):
for i in range(2, n):
if n % i == 0:
return False
return True
def get_primes(lower, upper):
for num in range(lower, upper):
if is_prime(num):
primes.append(num)
避免使用全局函数
如果您使用函数
get_primes
而不是附加到全局变量,而是使用适当的返回值,则将更易于使用。def get_primes(lower, upper):
primes = []
for num in range(lower, upper):
if is_prime(num):
primes.append(num)
return primes
primes = get_primes(5, 56) # get_primes(500000, 560000)
print primes
(再次)更多功能
您可以添加函数
get_twin_primes
以返回...双素数。if __name__ == "__main__":
在Python中,使用
if __name__ == "__main__":
将您的函数/类定义与实际使用它的代码分开是实用(传统)的。这样就可以通过导入实现代码的重用:这些函数/类可以在不触发所有计算的情况下被其他模块使用。现在,整个代码如下所示: br />
更优美的循环
我强烈推荐内德·巴切德尔(Ned Batchelder)的精彩演讲,称为“像当地人一样循环”(以及他所做的任何其他演讲)。您将学到很多有关Python迭代的知识,并且基本上,每当您编写“ range(len(list))”时,都有一种更好的方法。
在您的情况下,您会发现各种遍历列表中连续项的方法如下:
import math
def is_prime(n):
for i in range(2, n):
if n % i == 0:
return False
return True
def get_primes(lower, upper):
primes = []
for num in range(lower, upper):
if is_prime(num):
primes.append(num)
return primes
def get_twin_primes(primes):
twins = []
for i in range(0, len(primes) - 1):
if primes[i+1] - primes[i] == 2:
twins.append((primes[i], primes[i+1]))
return twins
if __name__ == "__main__":
primes = get_primes(5, 56) # get_primes(500000, 560000)
print primes
for a, b in get_twin_primes(primes):
print a, b
(在Python 2中,您可以使用
itertools.izip
而不是zip
,但是如果您正在学习Python,则可以应该尝试直接使用Python 3)。然后,可以使用列表推导轻松地重写此代码:
def get_twin_primes(primes):
twins = []
for p1, p2 in zip(primes, primes[1:]):
if p2 - p1 == 2:
twins.append((p1, p2))
return twins
更多列表推导
还可以在
get_primes
中使用列表推导法:def get_twin_primes(primes):
return [(p1, p2) for p1, p2 in zip(primes, primes[1:]) if p2 - p1 == 2]
使用内置函数(和生成器表达式)
您的函数可以使用内置的
all
以更简洁的方式编写。def get_primes(lower, upper):
return [num for num in range(lower, upper) if is_prime(num)]
您可以轻松地将其理解为“数字是质数,所有除法运算的结果都是非零余数”。
就像您以前的逻辑一样,但是您正在重用已经存在的内置函数,它会教给您新的知识事情:)
现在,整个代码如下:
def is_prime(n):
return all(n % i != 0 for i in range(2, n))
优化
简单但非常有效检查素数时的优化是使用以下事实:如果一个数字是复合的,那么对于任何一对因素,一个都小于或等于平方根,另一个大于或等于平方根,因此您不必无需检查大于您所考虑数字平方根的值。在那里,您可以编写类似以下内容的代码:
import math
def is_prime(n):
return all(n % i != 0 for i in range(2, n))
def get_primes(lower, upper):
return [num for num in range(lower, upper) if is_prime(num)]
def get_twin_primes(primes):
return [(p1, p2) for p1, p2 in zip(primes, primes[1:]) if p2 - p1 == 2]
if __name__ == "__main__":
primes = get_primes(5, 56) # get_primes(500000, 560000)
print primes
for a, b in get_twin_primes(primes):
print a, b
您可以做的另一种优化是使用
step
中range的get_primes
参数仅对奇数进行迭代(并添加“ 2” ”(如果需要,请在开头)。评论
\ $ \ begingroup \ $
这是一个很好的答案。对于OP,“更漂亮的循环”之前的所有内容现在都可以阅读,吸收和使用。此后的所有内容都是不错的建议,但是“沉没”可能需要更长的时间(因此,如果并非立即有意义,请不要气our)。
\ $ \ endgroup \ $
– brian_o
17年2月7日在19:02
\ $ \ begingroup \ $
很好的答案,只是对nitpick而言,您可能意味着itertools.izip可在Python 2上使用。
\ $ \ endgroup \ $
– niemmi
17年2月8日在7:43
\ $ \ begingroup \ $
我认为,严格地说,您对“除数不会大于平方根”的描述具有误导性。考虑12;它的一个因子为6,大于12的平方根。它更是“如果一个数字是复合的,对于任何一对因子,一个小于或等于平方根,另一个大于或等于平方根,因此您不需要检查大于平方根的值”。还是按照这些原则。最终结果是相同的;超过平方根时,停止测试。您也只能测试3个以上的奇数。
\ $ \ endgroup \ $
–乔纳森·莱弗勒(Jonathan Leffler)
17年2月8日在14:58
\ $ \ begingroup \ $
@niemmi这是正确的。我已经更新了答案。感谢您发现这一点。
\ $ \ endgroup \ $
– SylvainD
17年2月8日在15:01
\ $ \ begingroup \ $
@JonathanLeffler这是正确的。我已经更新了答案。感谢您发现这一点。
\ $ \ endgroup \ $
– SylvainD
17年2月8日在15:01
#2 楼
要继续从乔赛(Josay)的答案开始,有一种更好的方法来生成一个低于极限的素数。这是一个主要的筛子,其中最简单的是Eratosthenes筛子。您甚至可以将其编写为Python生成器,例如:
def prime_sieve(limit):
# Array denoting if the index is prime, initially assume all primes
prime = [True] * limit
# Set the starting conditions
prime[0] = prime[1] = False
for i, is_prime in enumerate(prime):
# No number smaller than i marked i as a multiple of it
if is_prime:
yield i
# Set all multiples of i as not prime, starting with i*i
for n in xrange(i * i, limit, i):
prime[n] = False
请注意,如果您使用的是Python 3.x,则需要用
xrange
替换range
。现在它是一个生成器,使用
zip
一次循环了两个,就像Josay所做的那样,有点浪费(我们需要两个生成器,每个生成器都保存了几乎相同的内部状态,因此占用了两倍的内存...),即使它们是列表是非常好的。只需将先前的状态保存在变量中就更容易了,就像这样:def prime_pairs(lower, upper):
# Initialize generator of primes up to upper
primes = prime_sieve(upper)
# Forward generator up to lower
p1 = next(primes)
while p1 < lower:
p1 = next(primes)
# Yield all twin primes
for p2 in primes:
if p2 - p1 == 2:
yield p1, p2
p1 = p2
if __name__ == "__main__":
lower = int(raw_input("Enter lower limit: "))
upper = int(raw_input("Enter upper limit: "))
for pair in prime_pairs(lower, upper):
print pair
在Python 3.x中用
print pair
替换print(pair)
,用raw_input
替换input
。 > 当您需要大量的底料时,底料筛方法的优势显而易见。使用乔赛(Josay)定义的
get_primes
函数,例如:In[23]: %timeit sum(get_primes(0, 100000))
1 loop, best of 3: 344 ms per loop
而原筛明显更快(大约25倍):
In [24]: %timeit sum(prime_sieve(100000))
10 loops, best of 3: 13.9 ms per loop
手头的任务也更快(这完全由质数的生成决定):
In [33]: %timeit get_twin_primes(get_primes(0, 100000))
1 loop, best of 3: 348 ms per loop
In [34]: %timeit list(prime_pairs(0, 100000))
100 loops, best of 3: 14.5 ms per loop
/>另一种情况是,您想要的素数都很大,但是您并不在乎较小的素数双胞胎。在这里Josay的方法胜出(因为生成器方法将始终遍历整个生成器,并将其转发到下限):
In [74]: %timeit get_twin_primes(get_primes(99900, 100000))
1000 loops, best of 3: 450 µs per loop
In [75]: %timeit list(prime_pairs(99900, 100000))
100 loops, best of 3: 14.7 ms per loop
99900被选择为此处的下限,以便该范围实际上包含一个质子孪生。
并显示与您相比,Josay的方法已经有多出色,这里是您的代码(包装在一个简单的函数中,并替换了
print
和yield
):def get_primes_op(lower, upper):
primes = []
for num in range(lower, upper):
for i in range(2, num):
if num % i == 0:
break
else:
primes.append(num)
return primes
def twin_primes(primes):
for i in range(0, len(primes) - 1):
if primes[i + 1] - primes[i] == 2:
yield str(primes[i]), str(primes[i + 1])
In [54]: %timeit list(twin_primes(get_primes_op(0, 100000)))
1 loop, best of 3: 1min 38s per loop
In [77]: %timeit list(twin_primes(get_primes_op(99900, 100000)))
1 loop, best of 3: 252 ms per loop
从我的代码到Josay的代码,这是惊人的280倍的速度增长;对于使用大量素数的代码,这是我的代码的> 6500倍的速度增长;在我的代码中,Josay的速度是560倍,而速度却只有17倍。下限接近上限的情况,上限也很大。
因此,作为结论,选择适合该工作的工具。然后测试哪种工具实际上是满足您特定需求的合适工具。
如果您需要一长串的质数低于某个上限但下限较低(可能甚至为0),则可以使用素筛生成器是这样的方法。
如果需要带有任意上下限的素数列表,则实现显式
is_prime
函数会更快。评论
\ $ \ begingroup \ $
快速说明:如果下限值与要检查的范围的实际大小相比太大,则筛分方法可能会更慢。
\ $ \ endgroup \ $
– SylvainD
17年2月7日在16:50
\ $ \ begingroup \ $
太好了! (很遗憾,我无法第二次更新您的答案;-)
\ $ \ endgroup \ $
– SylvainD
17年2月7日在17:06
\ $ \ begingroup \ $
@Josay没关系,自从HNQ不久前提出这个问题以来,支持投票就一直在稳步进行:)
\ $ \ endgroup \ $
–地狱
17年2月7日在17:07
\ $ \ begingroup \ $
prime = [True] *限制Uhh,这似乎效率不高。为什么不使用集合(如果Python有它们)?像这种方法一样,搜索是O(1),但是空间是O(pi(n)),而不是O(n)。
\ $ \ endgroup \ $
–莫妮卡基金的诉讼
17-2-8在4:53
\ $ \ begingroup \ $
@QPaysTaxes是的,Python中有一些设置以及带有它的筛子的实现。我上次尝试使用它时,结果比此实现要慢。节省空间将是不错的选择,但是对于非常大的限制(> 1,000,000,000左右)来说,这只是一个问题。
\ $ \ endgroup \ $
–地狱
17年2月8日在7:41
评论
推荐读物:Eratosthenes筛子