Combinations and Permutations
When the problem reads 'pick K of N' or 'order all N', the right reflex in Python is `itertools.combinations` or `itertools.permutations`. Both are lazy iterators, so they enumerate huge search spaces without materializing them. This entry walks combinations, permutations, and `combinations_with_replacement`, plus when each is the right tool.
235 views
2
from itertools import combinations
team = ['ana', 'ben', 'cleo', 'dax']
# All 2-person pairs (order does not matter, no repeats).
pairs = list(combinations(team, 2))
print(pairs)
# [('ana', 'ben'), ('ana', 'cleo'), ('ana', 'dax'),
# ('ben', 'cleo'), ('ben', 'dax'), ('cleo', 'dax')]
# Count: C(4, 2) = 6
print(len(pairs)) # 6
# Works on any iterable, output is sorted by input position.
print(list(combinations('abcd', 3)))
# [('a', 'b', 'c'), ('a', 'b', 'd'), ('a', 'c', 'd'), ('b', 'c', 'd')]combinations(iterable, r) yields every r-length tuple of items in input order, with no repeats and no equivalent reorderings. The output count equals the binomial coefficient C(n, r), and the order matches the input order, so combinations of a sorted input is itself sorted. Reach for it whenever the question is 'how many ways to pick r things' and order does not matter (committee selection, edge enumeration in a graph, choosing K elements for a subset-sum check). The iterator is lazy: stopping the loop early avoids enumerating the rest.
from itertools import permutations
team = ['ana', 'ben', 'cleo']
# All orderings of the full team.
full = list(permutations(team))
print(full)
# [('ana', 'ben', 'cleo'), ('ana', 'cleo', 'ben'),
# ('ben', 'ana', 'cleo'), ('ben', 'cleo', 'ana'),
# ('cleo', 'ana', 'ben'), ('cleo', 'ben', 'ana')]
print(len(full)) # 6 = 3!
# Pick 2 with order: P(3, 2) = 6
pairs_ordered = list(permutations(team, 2))
print(pairs_ordered)
# [('ana', 'ben'), ('ana', 'cleo'), ('ben', 'ana'),
# ('ben', 'cleo'), ('cleo', 'ana'), ('cleo', 'ben')]permutations(iterable, r=None) yields every r-length ordered tuple. Defaulting r to the full length gives all n! orderings of the input. Permutations are the right tool when sequence matters: scheduling, ordering jobs, traveling-salesperson tours, anagram enumeration. The space grows fast (10! = 3.6M, 12! = 479M), so guard the call with a length cap or stream into a for loop with an early break.
from itertools import combinations_with_replacement, product
# 'how many ways to roll 2 dice and add up?'
# Order does not matter, repeats allowed.
rolls = list(combinations_with_replacement([1, 2, 3, 4, 5, 6], 2))
print(len(rolls)) # 21 = C(6 + 2 - 1, 2)
print(rolls[:4])
# [(1, 1), (1, 2), (1, 3), (1, 4)]
# 'how many ordered outcomes' = full Cartesian product.
outcomes = list(product([1, 2, 3, 4, 5, 6], repeat=2))
print(len(outcomes)) # 36 = 6 * 6
# product is also great for nested-loop avoidance:
for i, j in product(range(2), range(3)):
print(i, j, end=' ')
print()
# 0 0 0 1 0 2 1 0 1 1 1 2combinations_with_replacement(iterable, r) allows repeated elements but treats (1, 2) and (2, 1) as the same tuple, which is the right shape for multisets like dice rolls or coin denominations. product(*iterables, repeat=k) is the full Cartesian product (order matters and repeats allowed), and it is the cleanest way to flatten a stack of nested for loops. The four functions cover the standard four-way split: ordered or not, repeats or not. Pick the one whose count formula matches the problem and let itertools do the bookkeeping.
