Cross product of sets using recursion

The simplest recursive definition of the cartesian product I can think of looks like this. You can see that like yours, this has a loop — actually, two loops embedded in a list comprehension. Unlike yours, this can handle two or more sequences:

def product(*seqs):
    if not seqs:
        return [[]]
    else:
        return [[x] + p for x in seqs[0] for p in product(*seqs[1:])]

Here’s a walk-through to give you a sense of how it works. By definition, the cartesian product of an empty sequence (product()) is a sequence containing the empty sequence. In other words, product() == [[]]see here for why.

Now let’s say we call product([1, 2, 3])seqs is non-empty, so the return value is the result of the list comprehension. In this case, product(*seqs[1:]) == product(*[]) == product() == [[]], so the list comprehension is equivalent to this:

[[x] + p for x in seqs[0] for p in [[]] ]

Which is equivalent to all of these:

[[x] + [] for x in seqs[0]]
[[x] for x in seqs[0]]
[[1], [2], [3]]

Adding another sequence, we have product([4, 5, 6], [1, 2, 3]). Now the list comprehension looks like this:

[[x] + p for x in [4, 5, 6] for p in product(*seqs[1:])]
[[x] + p for x in [4, 5, 6] for p in [[1], [2], [3]]]
[[4, 1], [4, 2], [4, 3], [5, 1], [5, 2], [5, 3], [6, 1], [6, 2], [6, 3]]

The pattern continues from there; for each additional sequence, each of the values in the sequence is prepended to each of the values in the cartesian product of the following sequences.

Leave a Comment