Сумма товаров всех пар в списке - PullRequest
2 голосов
/ 28 октября 2019

Я хочу суммировать произведения всех отдельных пар в списке. Я написал следующий код, который правильно считает это: 1*2 + 1*3 + 1*4 + 2*3 + 2*4 + 3*4

l = [1, 2, 3, 4]
def sum_of_pairs(ls):
    return sum([x * y for x in ls for y in ls[ls.index(x) + 1:]])
print(sum_of_pairs(l))

Когда я пытаюсь изменить список на l = [1, 1, 1, 1], он возвращает 12 (а не 6).

Почему этопроисходит?

Ответы [ 2 ]

2 голосов
/ 28 октября 2019

Вместо этого используйте следующий краткий подход с функциями itertools.starmap и itertools.combinations:

from itertools import starmap, combinations
from operator import mul

def sum_of_pairs(lst):
    return sum(starmap(mul, combinations(lst, 2)))

print(sum_of_pairs([1, 2, 3, 4]))   # 35
print(sum_of_pairs([1, 1, 1, 1]))   # 6
2 голосов
/ 28 октября 2019

Непосредственная проблема заключается в том, что вы используете ls.index, чтобы найти, где начать внутренний цикл. list.index возвращает индекс первого соответствующего элемента, поэтому будет делать только то, что вам нужно, если в списке нет дубликатов. Другая проблема заключается в том, что он выполняет линейный поиск в вашем списке для каждой итерации внешнего цикла, что делает ваш алгоритм излишне неэффективным.

Доступен ряд обходных путей. Простым является использование enumerate для указания нужного индекса:

sum(n * ls[j] for i, n in enumerate(ls) for j in range(i + 1, len(ls)))

Или вы можете использовать пару объектов range:

sum(ls[i] * ls[j] for i in range(len(ls)) for j in range(i + 1, len(ls)))

Создание range обычно дешевле, чем копирование всего подсписка каждый раз, как с

 sum(x * y for i, x in enumerate(ls) for y in ls[i + 1:])

В качестве альтернативы, вы можете использовать itertools.combinations, чтобы сгенерировать значения для вас более эффективно:

sum(x * y for x, y in itertools.combinations(ls, 2))
...