Лучшие способы найти пары с суммой N

1

Есть ли более быстрый способ написать это, функция принимает список и значение, чтобы найти пары числовых значений в этом списке, которые суммируются с 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
    Вам нужны сами цифры или индексы?
  • 0
    Сами числа держатся на im gunna edit и приводим пример
Показать ещё 2 комментария
Теги:

4 ответа

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, но это будет труднее читать, и не будет никакого повышения эффективности.

  • 0
    Используя список [1,2,3,4,5,5,6,7,8,9] и значение 10 , ваш алгоритм не возвращает пару (5,5)
  • 0
    @Neb Правда, но я думаю, что ОП не хочет, чтобы пары с обоими числами были одинаковыми, хотя я мог бы неправильно это понять. Однако, как я уже сказал выше, это легко исправить, отрегулировав условие break .
Показать ещё 10 комментариев
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 " Two pointers technique и он работает следующим образом. Прежде всего, отсортируйте массив. Это накладывает минимальное время работы O (nlogn). Затем установите два указателя, один из которых указывает на начало массива l и другие, указывающие на его последний элемент r (имя указателя для левого и правого).

Теперь посмотрим на список. Если сумма значений, возвращаемых в позиции l и r, ниже требуемого значения, нам нужно увеличить l. Если это больше, нам нужно уменьшить r.

Если значение v[l] + v[r] == value чем мы можем увеличивать/уменьшать как l и r так как в любом случае мы хотим пропустить комбинацию значений (v[l], v[r]) t хочу дублировать.

  • 0
    Это круто, мне нравится это решение так же, как и другое! Этот тип имеет те же принципы бинарного поиска, по крайней мере для меня, это то, на что это похоже! Спасибо это круто
  • 1
    Время выполнения равно O (nlogn), поскольку сортировка занимает O (nlogn), а остальная часть кода выполняется за линейное время.
Показать ещё 5 комментариев
2

Сроки: это на самом деле медленнее, чем другие 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
  • 0
    Ааааааааааааааааааааааааааааааааааааааааа в жизни, спасибо за пример, это, вероятно, быстрее, но я еще не проверял
  • 0
    @ Anonymous3.1415 добавил более общий подход - из-за сортировки требуется больше вычислительной мощности
Показать ещё 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))

Ещё вопросы

Сообщество Overcoder
Наверх
Меню