Explain Codes LogoExplain Codes Logo

Get all possible (2^N) combinations of a list’s elements, of any length

python
combinations
recursion
iterators
Alex KataevbyAlex Kataev·Dec 10, 2024
TLDR

To generate every combination from a list, use Python's itertools.chain and itertools.combinations in a comprehension:

from itertools import combinations, chain # Let's "chain" up all combinations! combos = list(chain.from_iterable(combinations([1, 2, 3], r) for r in range(4)))

Voila! This generates the power set of [1, 2, 3], providing 2^N combinations, inclusively from an empty set to a full set.

Broaden your toolkit

Catering to Originality: Handling Uniqueness and Order

Before hopping into combination generation, ensure the input list is sorted and unique:

from itertools import combinations, chain # Because who likes repetition without purpose, right? data = [1, 1, 2, 3] unique_data = sorted(set(data)) combos = list(chain.from_iterable(combinations(unique_data, r) for r in range(len(unique_data)+1)))

This magic trick presents a list of ordered combinations while dodging repetitions. Now we're talking!

Just Right: Generating Specific Length Combinations

Need combinations of a specific length? Python's got your back:

from itertools import combinations # Goldilocks approves! Not too long, not too short... specific_length_combos = list(combinations([1, 2, 3], 2))

You will get combinations that only Goldilocks would love - length 2, like [1, 2], [1, 3], [2, 3].

Channel the snake: Recursive Python Approach

Are you those who dig custom implementations? Time to get recursive:

def combinations_recursive(seq): if not seq: return [[]] # Here python tries its tail. And then its whole body. rest = combinations_recursive(seq[1:]) return rest + [[seq[0]] + combo for combo in rest] combos = combinations_recursive([1, 2, 3])

This custom method gives you an inside peek at how combinations are built from scratch.

More tricks to master

Squeezing more: Compressive Powerset with product

An alternative approach to generating all combinations is via itertools.product and itertools.compress:

from itertools import product, compress # Items feeling compressed? Nah, they're just combining! items = [1, 2, 3] combos = [tuple(compress(items, mask)) for mask in product(*[[0, 1]]*len(items))]

By leveraging a binary mask from itertools.product, you can emulate an element's presence or absence in the combinations.

Back to Origins: Recursive combination generation

Have delight with recursion:

def powerset_recursive(s): if not s: return [s] x, *xs = s # Hey there, recursion! Haven't seen you since... the last call. without_x = powerset_recursive(xs) return without_x + [[x] + subset for subset in without_x] combos = powerset_recursive([1, 2, 3])

The equation is simple: recursion + subsets = all potential combinations.