有没有更快的方法来编写此函数,该函数需要一个列表和一个值以在该列表中查找成对的N对数字值,这些对的总和为N,而没有重复项。我试图通过使用集合而不是使用列表来使其更快本身(但是我使用的是count(),但我知道这是线性时间),我知道有什么建议可能是一种方法
def pairsum_n(list1, value):
set1 = set(list1)
solution = {(min(i, value - i) , max(i, value - i)) for i in set1 if value - i in set1}
solution.remove((value/2,value/2)) if list1.count(value/2) < 2 else None
return solution
"""
Example: value = 10, list1 = [1,2,3,4,5,6,7,8,9]
pairsum_n = { (1,9), (2,8), (3,7), (4,6) }
Example: value = 10, list2 = [5,6,7,5,7,5,3]
pairsum_n = { (5,5), (3,7) }
"""
答案 0 :(得分:3)
尝试一下,运行时间O(nlogn):
v = [1, 2, 3, 4, 5, 6, 7, 8, 9]
l = 0
r = len(v)-1
def myFunc(v, value):
ans = []
% this block search for the pair (value//2, value//2)
if value % 2 == 0:
c = [i for i in v if i == value // 2]
if len(c) >= 2:
ans.append((c[0], c[1]))
v = list(set(v))
l = 0
r = len(v)-1
v.sort()
while l<len(v) and r >= 0 and l < r:
if v[l] + v[r] == value:
ans.append((v[l], v[r]))
l += 1
r -= 1
elif v[l] + v[r] < value:
l += 1
else:
r -= 1
return list(set(ans))
它称为Two pointers technique
,其工作原理如下。首先,对数组进行排序。这强加了最小运行时间O(nlogn)。然后设置两个指针,一个指针指向数组l
的开始,另一个指针指向数组的最后一个元素r
(指针名称分别用于左侧和右侧)。
现在,查看列表。如果在位置l和r返回的值之和小于我们要寻找的值,则需要递增l
。如果更大,则需要递减r
。
如果v[l] + v[r] == value
可以同时增加/减少l
或r
,因为在任何情况下我们都希望跳过值(v[l], v[r])
的组合,因为我们不想重复。
答案 1 :(得分:3)
您的方法非常好,只需进行一些调整即可使其更有效。 itertools
很方便,但实际上并不适合此任务,因为它会产生很多不需要的对。输入列表较小可以,但是输入列表较大则太慢。
我们可以通过以下方法来避免产生重复项:依次使用数字集,在使用i >= value/2
来消除重复项之后停止。
def pairsum_n(list1, value):
set1 = set(list1)
list1 = sorted(set1)
solution = []
maxi = value / 2
for i in list1:
if i >= maxi:
break
j = value - i
if j in set1:
solution.append((i, j))
return solution
请注意,原始list1
未被修改。此函数中的分配创建一个新的本地list1
。如果您确实希望在输出中使用(value/2, value/2)
,只需更改break
条件。
这是一个稍微紧凑的版本。
def pairsum_n(list1, value):
set1 = set(list1)
solution = []
for i in sorted(set1):
j = value - i
if i >= j:
break
if j in set1:
solution.append((i, j))
return solution
可以进一步压缩,例如使用itertools.takewhile
,但它很难阅读,效率也没有任何提高。
答案 2 :(得分:2)
时间:实际上,这比其他两种解决方案要慢。由于生成的组合数量很多,但实际上并不需要,因此列表越大,情况就越糟。
您可以使用itertools.combinations为您生成2元组组合。
如果它们与您的value
相匹配,则将它们放入集合中,然后作为集合/列表返回:
from itertools import combinations
def pairsum_n(list1, value):
"""Returns the unique list of pairs of combinations of numbers from
list1 that sum up `value`. Reorders the values to (min_value,max_value)."""
result = set()
for n in combinations(list1, 2):
if sum(n) == value:
result.add( (min(n),max(n)) )
return list(result)
# more ugly one-liner:
# return list(set(((min(n),max(n)) for n in combinations(list1,2) if sum(n)==value)))
data = [1,2,3,4,5,6,6,5,4,3,2,1]
print(pairsum_n(data,7))
输出:
[(1, 6), (2, 5), (3, 4)]
有趣的小事情,加上一些排序开销,您可以一次获得所有内容:
def pairsum_n2(data, count_nums=2):
"""Generate a dict with all count_nums-tuples from data. Key into the
dict is the sum of all tuple-values."""
d = {}
for n in (tuple(sorted(p)) for p in combinations(data,count_nums)):
d.setdefault(sum(n),set()).add(n)
return d
get_all = pairsum_n2(data,2) # 2 == number of numbers to combine
for k in get_all:
print(k," -> ", get_all[k])
输出:
3 -> {(1, 2)}
4 -> {(1, 3), (2, 2)}
5 -> {(2, 3), (1, 4)}
6 -> {(1, 5), (2, 4), (3, 3)}
7 -> {(3, 4), (2, 5), (1, 6)}
2 -> {(1, 1)}
8 -> {(2, 6), (4, 4), (3, 5)}
9 -> {(4, 5), (3, 6)}
10 -> {(5, 5), (4, 6)}
11 -> {(5, 6)}
12 -> {(6, 6)}
然后通过以下方式访问所需的那个:
print(get_all.get(7,"Not possible")) # {(3, 4), (2, 5), (1, 6)}
print(get_all.get(17,"Not possible")) # Not possible
答案 3 :(得分:1)
还有另一种解决方案,它比我刚才写的要快得多,不如@PM 2Ring的答案快>
def pairsum_n(list1, value):
set1 = set(list1)
if list1.count(value/2) < 2:
set1.remove(value/2)
return set((min(x, value - x) , max(x, value - x)) for x in filterfalse(lambda x: (value - x) not in set1, set1))