Batu Lab NotesPractical developer guides

Count combinations before materializing them

By Batu ยท English technical notes

Also published in our Blogger archive.

Before creating combinations, calculate how many there can be. In this example, five feature flags are available and the program wants every unordered pair. math.comb(5, 2) returns 10, which matches the number of tuples later returned by itertools.combinations(flags, 2). That count permits a concrete guard: this code would decline to materialize if there were more than 20 candidate pairs.

The threshold is a local product decision, not a universal memory or performance guarantee. The memory cost also depends on the values, tuple size, and the work performed for each result. For inputs that might be large, count first, choose a suitable limit, and consider streaming the combinations instead of placing them in a list. math.comb(n, k) returns zero when k is greater than n, while negative arguments raise ValueError; validate externally supplied sizes before calling it.

math.comb was added in Python 3.8. itertools.combinations is available in supported Python 3 releases and emits tuples in input order when the input is sorted. It chooses positions, not merely unique values, so duplicate input values can result in duplicate-looking outputs. The assertions establish the mathematical count and expected endpoints for the fixed tuple only.

AI-assistance disclosure: this article was drafted with AI assistance; tune the limit using measurements from the actual application.

Sources: Python math.comb documentation and Python itertools.combinations documentation.

from itertools import combinations
from math import comb

flags = ("audit", "cache", "email", "export", "search")
pair_count = comb(len(flags), 2)
limit = 20

assert pair_count == 10
assert pair_count <= limit
pairs = list(combinations(flags, 2))
assert len(pairs) == pair_count
assert pairs[0] == ("audit", "cache")
assert pairs[-1] == ("export", "search")

print(f"candidate_pairs={pair_count}")
print(f"materialized_pairs={len(pairs)}")
candidate_pairs=10
materialized_pairs=10