Sum of products of all pairs in a list

Viewed 81

I want to sum the products of all distinct pairs in a list. I wrote the following code that counts correctly this: 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))

When I try to change list to l = [1, 1, 1, 1] it returns 12 (and not 6).

Why is this happening?

2 Answers

The immediate issue is that you are using ls.index to find where to start the inner loop. list.index returns the index of the first matching item, so will only do what you want if there are no duplicates in the list. Another problem is that it performs a linear search of your list for every iteration of the outer loop, making your algorithm needlessly inefficient.

A number of workarounds are available. A simple one is using enumerate to follow along with the index that you want:

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

Or you could use a pair of range objects:

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

Creating a range is generally cheaper than copying out the entire sublist every time, as with

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

Alternatively, you could use itertools.combinations to generate the values for you a bit more efficiently:

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

Instead, use the following short approach with itertools.starmap and itertools.combinations features:

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
Related