Есть ли более быстрый способ написать это, функция принимает список и значение, чтобы найти пары числовых значений в этом списке, которые суммируются с 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) }
"""
Ваш подход довольно хорош, ему просто нужно несколько настроек, чтобы сделать его более эффективным. 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
, но это будет труднее читать, и не будет никакого повышения эффективности.
[1,2,3,4,5,5,6,7,8,9]
и значение 10
, ваш алгоритм не возвращает пару (5,5)
break
.
Попробуйте это, время работы 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
" Two pointers technique
и он работает следующим образом. Прежде всего, отсортируйте массив. Это накладывает минимальное время работы O (nlogn). Затем установите два указателя, один из которых указывает на начало массива l
и другие, указывающие на его последний элемент r
(имя указателя для левого и правого).
Теперь посмотрим на список. Если сумма значений, возвращаемых в позиции l и r, ниже требуемого значения, нам нужно увеличить l
. Если это больше, нам нужно уменьшить r
.
Если значение v[l] + v[r] == value
чем мы можем увеличивать/уменьшать как l
и r
так как в любом случае мы хотим пропустить комбинацию значений (v[l], v[r])
t хочу дублировать.
Сроки: это на самом деле медленнее, чем другие 2 решения. Из-за количества произведенных комбинаций, но фактически не требуется, становится все хуже, чем больше списков.
Вы можете использовать itertools.combinations для создания комбинаций из 2-х кортежей для вас.
Поместите их в набор, если они соответствуют вашему value
, а затем верните его как set/list:
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
Иметь другое решение, это намного быстрее, чем тот, который я только что написал, не так быстро, как @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))