this is a variant:
from collections import Counter
def sum_target(lst, target):
unique_list = list(set(lst))
counts = Counter(lst)
remainders = {number: target-number for number in unique_list}
match = set()
for a, b in remainders.items():
if b in remainders:
match.add(tuple(sorted((a, b))))
# restore multiplicities:
ret = []
for a, b in match:
if a != b:
mult = counts[a] * counts[b]
ret.extend([a, b] for _ in range(mult))
ret.extend([b, a] for _ in range(mult))
# ret.append((mult, (a, b)))
# ret.append((mult, (b, a)))
else:
mult = counts[a] * (counts[a]-1)
ret.extend([a, a] for _ in range(mult))
# if mult != 0:
# ret.append((mult, (a, b)))
return ret
not entirely sure the multiplicities are handled the way you want (in your example they are twice the ones i get... why are there 6 versions of [5, 5]
? with three 5
s in your original list you only get 3 pairs...)
if you have many duplicates in your list then given the length of your output i suggest changing the last part of my code to
# restore multiplicities:
ret = []
for a, b in match:
if a != b:
mult = counts[a] * counts[b]
ret.append((mult, (a, b)))
ret.append((mult, (b, a)))
else:
mult = counts[a] * (counts[a]-1)
if mult != 0:
ret.append((mult, (a, b)))
to get the result as
numbers = [1, 2, 4, 4, 4, 4, 5, 5, 5, 7, 7, 8, 8, 8, 9]
s = 10
print(sum_target(lst=numbers, target=s))
# [(3, (8, 2)), (3, (2, 8)), (6, (5, 5)), (1, (1, 9)), (1, (9, 1))]