[英]Python 2 lists of positive integers finding prime number
给定2个正整数列表,请找到从每个列表中选择一个数字的方式,以使它们的和为质数。
我的代码太慢了,因为我同时拥有list1和list 2,每个列表都包含50000个数字。 那么有什么方法可以使其更快,以便在数分钟而不是数天内解决? :)
# 2 is the only even prime number
if n == 2: return True
# all other even numbers are not primes
if not n & 1: return False
# range starts with 3 and only needs to go
# up the squareroot of n for all odd numbers
for x in range(3, int(n**0.5)+1, 2):
if n % x == 0: return False
return True
for i2 in l2:
for i1 in l1:
if isprime(i1 + i2):
n = n + 1 # increasing number of ways
s = "{0:02d}: {1:d}".format(n, i1 + i2)
print(s) # printing out
草图:
按照@Steve的建议,首先找出所有素数<= max(l1) + max(l2)
。 我们称该列表为primes
。 注意: primes
实际上不必是列表; 您可以一次生成最多一次的灌注 。
交换列表(如有必要),使l2
是最长的列表。 然后将其变成一个集合: l2 = set(l2)
。
排序l1
( l1.sort()
)。
然后:
for p in primes:
for i in l1:
diff = p - i
if diff < 0:
# assuming there are no negative numbers in l2;
# since l1 is sorted, all diffs at and beyond this
# point will be negative
break
if diff in l2:
# print whatever you like
# at this point, p is a prime, and is the
# sum of diff (from l2) and i (from l1)
l2
,例如,如果l2
是:
l2 = [2, 3, 100000000000000000000000000000000000000000000000000]
这是不切实际的。 它依赖于此,例如在您的示例中, max(max(l1), max(l2))
是“合理小”的。
哼! 您在评论中说,列表中的数字最长为5位数字。 因此,他们不到100,000。 您在一开始就说该列表每个都有50,000个元素。 因此,它们每个都包含100,000以下所有可能整数的一半,并且您将有大量的质数之和。 如果您想进行微优化,那一切就很重要;-)
无论如何,由于最大可能的总和小于200,000,所以任何筛分方法都将足够快-这将是运行时的重要部分。 这是其余的代码:
def primesum(xs, ys):
if len(xs) > len(ys):
xs, ys = ys, xs
# Now xs is the shorter list.
xs = sorted(xs) # don't mutate the input list
sum_limit = xs[-1] + max(ys) # largest possible sum
ys = set(ys) # make lookups fast
count = 0
for p in gen_primes_through(sum_limit):
for x in xs:
diff = p - x
if diff < 0:
# Since xs is sorted, all diffs at and
# beyond this point are negative too.
# Since ys contains no negative integers,
# no point continuing with this p.
break
if diff in ys:
#print("%s + %s = prime %s" % (x, diff, p))
count += 1
return count
我不会提供gen_primes_through()
,因为它无关紧要。 从其他答案中选择一个,或编写自己的答案。
这是提供测试用例的便捷方法:
from random import sample
xs = sample(range(100000), 50000)
ys = sample(range(100000), 50000)
print(primesum(xs, ys))
注意:我正在使用Python3。如果您正在使用Python 2,请使用xrange()
而不是range()
。
在两次运行中,它们各自花费了大约3.5分钟。 那就是您一开始所要求的(“分钟而不是几天”)。 Python 2可能会更快。 返回的计数为:
219,334,097
和
219,457,533
当然,可能的总和为50000 ** 2 == 2,500,000,000。
此处讨论的所有方法(包括您的原始方法)所花费的时间与两个列表长度的乘积成比例。 所有的摆弄都是为了减少常数因子。 这是对您原始作品的巨大改进:
def primesum2(xs, ys):
sum_limit = max(xs) + max(ys) # largest possible sum
count = 0
primes = set(gen_primes_through(sum_limit))
for i in xs:
for j in ys:
if i+j in primes:
# print("%s + %s = prime %s" % (i, j, i+j))
count += 1
return count
也许您会更好地理解这一点。 为什么会有很大的改善? 因为它用快速的查找设置代替了昂贵的isprime(n)
函数。 它仍然需要与len(xs) * len(ys)
成正比的时间,但是通过用非常便宜的运算代替非常昂贵的内循环运算,可以削减“比例常数”。
而且,实际上,在许多情况下, primesum2()
也比我的primesum()
更快。 在您的特定情况下 ,使primesum()
更快的原因是,只有不到18,000个质数小于200,000。 因此,遍历素数(与primesum()
一样)比遍历具有50,000个元素的列表要快得多。
针对此问题的“快速”通用功能将需要根据输入选择不同的方法。
您应该使用Eratosthenes筛子来计算素数。
您还在为每种可能的总和组合计算素数。 而是考虑从列表中求和可以找到最大值。 生成所有素数直至该最大值的列表。
在添加数字的同时,您可以查看数字是否出现在素数列表中。
我会在每个范围内找到最高的数字。 素数的范围是最大数字的总和。
这是筛选素数的代码:
def eras(n):
last = n + 1
sieve = [0, 0] + list(range(2, last))
sqn = int(round(n ** 0.5))
it = (i for i in xrange(2, sqn + 1) if sieve[i])
for i in it:
sieve[i * i:last:i] = [0] * (n // i - i + 1)
return filter(None, sieve)
查找最多为10000000的素数大约需要3秒钟。然后,我将使用与生成总和相同的n ^ 2
算法。 我认为有一个n logn
算法,但我无法提出。
它看起来像这样:
from collections import defaultdict
possible = defaultdict(int)
for x in range1:
for y in range2:
possible[x + y] += 1
def eras(n):
last = n + 1
sieve = [0, 0] + list(range(2, last))
sqn = int(round(n ** 0.5))
it = (i for i in xrange(2, sqn + 1) if sieve[i])
for i in it:
sieve[i * i:last:i] = [0] * (n // i - i + 1)
return filter(None, sieve)
n = max(possible.keys())
primes = eras(n)
possible_primes = set(possible.keys()).intersection(set(primes))
for p in possible_primes:
print "{0}: {1} possible ways".format(p, possible[p])
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.