What shall I wear?
Lerato is packing for a school trip to the mountains. She takes 3 T-shirts — red, blue and yellow — and 2 pairs of trousers — black and brown. "How many different outfits can I make?" she wonders.
She starts calling them out: "Red and black. Blue and brown. Yellow and black. Red and brown…" and then she stops. Has she found them all? Has she said one twice? It is hard to tell when the possibilities come out in a muddle.
The same problem turns up everywhere: How many ways can three friends stand in a row for a photo? How many handshakes are there when five friends meet? How many different meals can you order from a menu? In this lesson you will learn to answer such questions systematically — with a method that makes sure nothing is missed and nothing is counted twice.
The secret: fix one thing at a time
Here is the trick. Instead of jumping around, hold the first choice still and run through every second choice. Then move to the next first choice and do it again.
- Red T-shirt: with black, with brown.
- Blue T-shirt: with black, with brown.
- Yellow T-shirt: with black, with brown.
That is 6 outfits, and we can see that we have them all: every T-shirt appears, and with each T-shirt every pair of trousers appears.
Three ways to organise a list
Definition— organised list
An organised list writes down the possibilities in an order that follows a rule, so that you can check every possibility is there exactly once.
Definition— table
A table puts the first choices in the rows and the second choices in the columns. Every box is one possibility.
| black | brown | |
|---|---|---|
| red | red + black | red + brown |
| blue | blue + black | blue + brown |
| yellow | yellow + black | yellow + brown |
Definition— tree diagram
A tree diagram shows each choice as a branch. From the start, one branch goes out for each first choice; from the end of each of those, one branch goes out for each second choice. Each path from the start to an end is one possibility.
Worked examples
Example— Routes to market
From Farida's village there are 3 paths down to the river, and from the river 4 roads to the market. A route is a path followed by a road. For each of the 3 paths there are 4 roads to choose next, so there are routes.
Example— A meal with three choices
A school canteen offers 2 soups, 3 main dishes and 2 drinks. A tree diagram now has three levels: 2 branches, then 3 from each (6 so far), then 2 from each of those. That makes meals.
Example— Three friends in a row
Ana, Bo and Chen line up for a photo. Who stands first? There are 3 choices. Once the first place is filled, 2 people are left for the second place, and then just 1 for the last place. The tree has paths:
ABC, ACB, BAC, BCA, CAB, CBA.
Here order matters: Ana–Bo–Chen is a different photo from Chen–Bo–Ana. With a fourth friend, Dina, there are ways.
Example— Five friends shake hands
Amara, Ben, Carlos, Divya and Emil meet at a science fair, and every two of them shake hands once. List them systematically, one person at a time, only listing handshakes with people later in the list:
- Amara with Ben, Carlos, Divya, Emil: 4
- Ben with Carlos, Divya, Emil: 3 (his handshake with Amara is already listed)
- Carlos with Divya, Emil: 2
- Divya with Emil: 1
That is handshakes. Here order does not matter: "Amara shakes Ben's hand" and "Ben shakes Amara's hand" are the same handshake.
Why the rules work
Theorem— The product rule
If a first choice can be made in ways, and then a second choice in ways (whatever the first choice was), then there are ways to make both choices.
Proof
Draw the tree diagram. There are first branches. Each of them ends in second branches. So there are ( times) paths, and each path is one way of making both choices.
∎
Why does the organised list never miss a possibility or repeat one? Take any possibility, say blue T-shirt with brown trousers. It has exactly one T-shirt, so it lives on exactly one first branch (the blue one); and on that branch it has exactly one pair of trousers, so it is exactly one of the endings. Every possibility is on the tree once — not zero times, not twice. Notice that this needs the T-shirts to be different from each other, and the trousers too: two identical T-shirts would give outfits that look the same.
Theorem— Arranging in a row
Three different objects can be arranged in a row in exactly ways. In the same way, different objects can be arranged in ways.
Proof
Fill the places from left to right. There are 3 choices for the first place. Whatever we chose, 2 objects are left for the second place, and then 1 for the last. By the product rule (used twice) there are ways. For objects the same argument gives , a number written and called " factorial".
∎
Theorem— Counting handshakes
When people each shake hands once with every other person, there are handshakes.
Proof
Each person shakes hands, so counting person by person gives . But every handshake involves two people, so this counts every handshake twice — once for each of the two people. So the number of handshakes is .
∎
For the five friends: , just as our list showed.
Explore: handshakes as a network
Draw each friend as a dot and each handshake as a line between two dots. Five friends who all shake hands make the network below. Select a dot to see its handshakes: every dot has 4 lines. The total of the numbers on the dots is , but there are only 10 lines — each line has two ends.
Five friends, ten handshakes
Each dot is a friend; each line is a handshake.
4 + 4 + 4 + 4 + 4 = 20 = 2 × 10
Every edge has two ends, so it adds exactly 2 to the total: the degrees add up to 20 = 2 × 10. This is the handshake lemma, Σ deg(v) = 2|E|.
Vertices of odd degree: 0. This number is always even, because the total is even.
Listing helps with chance
Roll two dice, one red and one blue. Which total is most likely? List the results systematically in a table: 6 results for the red die, times 6 for the blue, makes equally likely results. A total of 7 appears in 6 of the boxes (1 + 6, 2 + 5, 3 + 4, 4 + 3, 5 + 2, 6 + 1) — more than any other total. A total of 2 appears in just one box. Run the experiment and watch the computer's rolls approach the exact numbers from the list.
Which total comes up most often?
Roll two dice and add them: sums near 7 come up most often, just as counting the 36 outcomes predicts.
- simulated frequency
- exact (ways out of 36)
Sum 7
Simulated15.2%
Exact6/36 ≈ 16.7%
Sum 12
Simulated2.9%
Exact1/36 ≈ 2.8%
Simulation of 1,000 rolls of two dice: a sum of 7 came up 15.2% of the time. The exact probability is 6/36, about 16.7%.
Evidence, not proofThis is a simulation of 1,000 rolls of two dice (run 1) using a seeded random number generator. It is evidence, not proof: the exact values come from mathematics, and a new run gives slightly different numbers.
There are 6 ways to make 7 (1+6, 2+5, 3+4, 4+3, 5+2, 6+1) but only one way to make 12 (6+6).
Gideon's true-or-false game
Gideon only plays with statements that are exactly true or exactly false. Decide, and give a reason.
- "With 4 T-shirts and 3 pairs of trousers there are 7 outfits." — False. There are . Adding counts the clothes, not the outfits.
- "Ana, Bo and Chen can stand in a row in exactly 6 ways." — True. We listed all of them, and the tree shows there are no others.
- "If I can't think of any more outfits, I must have found them all." — False. Not being able to think of more is not a proof. A system is.
- "Five friends make 20 handshakes." — False. 20 counts each handshake twice; there are 10.
For grown-ups · the same ideas in Lean, a proof checker
In Lean, an organised list is a recipe. pairs takes the first choices one at a time and, for each, runs through all the second choices — exactly like a tree diagram:
/-- The organised list of all pairs `(a, b)`: take the first choices one at a time, and for each
one run through all the second choices — exactly what a tree diagram does. -/
def pairs {α β : Type} : List α → List β → List (α × β)
| [], _ => []
| a :: as, bs => bs.map (fun b => (a, b)) ++ pairs as bsLean can write out Lerato's six outfits and check them:
/-- The six outfits, listed shirt by shirt. Lean writes out the list itself and checks it. -/
theorem outfits :
pairs shirts trousers =
[("red", "black"), ("red", "brown"), ("blue", "black"), ("blue", "brown"),
("yellow", "black"), ("yellow", "brown")] := by
decideBut Lean can do more than check one example. It proves the product rule for all lists of choices, and proves that the organised list misses nothing, adds nothing and repeats nothing:
/-- **The product rule.** With `m` first choices and `n` second choices there are `m × n` pairs:
each first choice starts a branch with `n` endings. -/
theorem length_pairs {α β : Type} (as : List α) (bs : List β) :
(pairs as bs).length = as.length * bs.length := by
induction as with
| nil => simp [pairs]
| cons a as ih =>
simp only [pairs, List.length_append, List.length_map, ih, List.length_cons]
rw [Nat.succ_mul]
omega/-- **Nothing is missed, nothing is extra.** A pair is on the organised list exactly when its
first part is one of the first choices and its second part is one of the second choices. -/
theorem mem_pairs {α β : Type} (as : List α) (bs : List β) (a : α) (b : β) :
(a, b) ∈ pairs as bs ↔ a ∈ as ∧ b ∈ bs := by
induction as with
| nil => simp [pairs]
| cons x xs ih =>
simp only [pairs, List.mem_append, List.mem_map, ih, List.mem_cons, Prod.mk.injEq]
constructor
· rintro (⟨b', hb', rfl, rfl⟩ | ⟨ha, hb⟩)
· exact ⟨Or.inl rfl, hb'⟩
· exact ⟨Or.inr ha, hb⟩
· rintro ⟨rfl | ha, hb⟩
· exact Or.inl ⟨b, hb, rfl, rfl⟩
· exact Or.inr ⟨ha, hb⟩/-- **Nothing is counted twice.** If the first choices are all different and the second choices
are all different, every pair on the organised list is different. Pairs from different branches
differ in their first part; pairs on the same branch differ in their second part. -/
theorem nodup_pairs {α β : Type} (as : List α) (bs : List β)
(has : as.Nodup) (hbs : bs.Nodup) : (pairs as bs).Nodup := by
induction as with
| nil => simp [pairs]
| cons a as ih =>
rw [List.nodup_cons] at has
simp only [pairs]
rw [List.nodup_append]
refine ⟨?_, ih has.2, ?_⟩
· -- On the branch of `a`, pairs `(a, b₁)` and `(a, b₂)` differ because `b₁ ≠ b₂`.
exact List.Pairwise.map _ (fun b₁ b₂ hne h => hne (Prod.mk.inj h).2) hbs
· intro p hp q hq hpq
subst hpq
rw [List.mem_map] at hp
obtain ⟨b, _, rfl⟩ := hp
exact has.1 ((mem_pairs as bs a b).1 hq).1Arrangements in a row, first for Ana, Bo and Chen, and then for any number of different objects ( ways):
/-- Ana, Bo and Chen can stand in a row in exactly these 6 orders, all different. -/
theorem three_in_a_row :
arrangements ["Ana", "Bo", "Chen"] =
[["Ana", "Bo", "Chen"], ["Bo", "Ana", "Chen"], ["Bo", "Chen", "Ana"],
["Ana", "Chen", "Bo"], ["Chen", "Ana", "Bo"], ["Chen", "Bo", "Ana"]] ∧
(arrangements ["Ana", "Bo", "Chen"]).Nodup := by
decide/-- **`n` different objects can be arranged in a row in `n!` ways.** Arrange the other `n − 1`
objects (in `(n − 1)!` ways), then choose one of the `n` places for the last object. -/
theorem length_arrangements {α : Type} (xs : List α) :
(arrangements xs).length = factorial xs.length := by
induction xs with
| nil => rfl
| cons x xs ih =>
simp only [arrangements]
rw [length_flatMap_const _ _ (xs.length + 1)
(fun ws hws => by rw [length_insertEverywhere, mem_arrangements_length xs ws hws]),
ih, List.length_cons, factorial, Nat.mul_comm]Handshakes: when person number arrives, they shake hands with the people already there. To avoid dividing, the theorem says , which is the same as handshakes:
/-- The handshakes among people `0, 1, …, n - 1`, listed systematically: when person `n`
arrives, they shake hands with each of the `n` people already there. Each handshake `(i, j)`
is listed once, with `i < j`. -/
def handshakes : Nat → List (Nat × Nat)
| 0 => []
| n + 1 => handshakes n ++ (List.range n).map (fun i => (i, n))/-- **Counting handshakes.** Among `n` people there are `n × (n − 1) ÷ 2` handshakes; we state it
without division as `2 × handshakes + n = n × n`. (When person `n + 1` arrives, `n` new
handshakes appear.) -/
theorem handshakes_count (n : Nat) : 2 * (handshakes n).length + n = n * n := by
induction n with
| zero => rfl
| succ n ih =>
simp only [handshakes, List.length_append, List.length_map, List.length_range]
rw [Nat.succ_mul_succ]
omega/-- Five friends make 10 handshakes. Counting each person's 4 handshakes gives `5 × 4 = 20`,
which counts every handshake twice. -/
theorem five_friends : (handshakes 5).length = 10 ∧ 5 * 4 = 2 * 10 := by
decideMathlib, Lean's large mathematics library, has all of these too: List.product, List.permutations and the counting rules for them, and Nat.choose for choosing 2 people out of .
Margaret's lists — and a guessing game
"""Organised lists: outfits, arrangements and handshakes.
A computer is very good at following a system. The program lists outfits
branch by branch, as in a tree diagram, and checks the product rule m x n
for every m and n up to 6. It lists every order of 3 and of 4 children in a
row. Finally it counts handshakes two ways - as ordered pairs (who reaches
out first) and as unordered pairs - to show the double counting.
Runs unchanged in the browser (Pyodide) using only the standard library.
"""
import textwrap
from itertools import combinations, permutations, product
SHIRTS = ["red", "blue", "yellow"]
TROUSERS = ["black", "brown"]
def tree_list(first, second):
"""List every (first, second) pair, branch by branch."""
return [(a, b) for a in first for b in second]
def product_rule_holds(max_size):
"""Check len(tree_list) == m * n for all 1 <= m, n <= max_size."""
for m in range(1, max_size + 1):
for n in range(1, max_size + 1):
listed = tree_list(range(m), range(n))
if len(listed) != m * n or len(set(listed)) != m * n:
return False
return True
def rows(children):
"""Every order of the children in a row, as strings."""
return ["".join(p) for p in permutations(children)]
def handshakes(n):
"""Each handshake among n people once: pairs (i, j) with i < j."""
return list(combinations(range(1, n + 1), 2))
def ordered_pairs(n):
"""Pairs (i, j) of different people where order matters."""
return [(i, j) for i, j in product(range(1, n + 1), repeat=2) if i != j]
def main():
outfits = tree_list(SHIRTS, TROUSERS)
print("Tree diagram, branch by branch:")
for shirt in SHIRTS:
ends = ", ".join(f"{s}+{t}" for s, t in outfits if s == shirt)
print(f" {shirt:<7}-> {ends}")
print(f"{len(SHIRTS)} shirts x {len(TROUSERS)} trousers"
f" = {len(outfits)} outfits, all different: "
f"{len(set(outfits)) == len(outfits)}")
print(f"Product rule m x n checked for all m, n up to 6:"
f" {product_rule_holds(6)}")
print()
print("Ana, Bo and Chen in a row: " + " ".join(rows("ABC")))
four = rows("ABCD")
print(f"Four children: {len(four)} orders, all different:"
f" {len(set(four)) == len(four)}")
print()
print("People handshakes ordered pairs n x (n - 1) / 2")
for n in range(2, 9):
print(f"{n:>6} {len(handshakes(n)):>10} {len(ordered_pairs(n)):>13}"
f" {n * (n - 1) // 2:>15}")
print("Ordered pairs count every handshake twice: A-B and B-A.")
print()
print(textwrap.fill(
"Epistemic status: exact and exhaustive for the sizes shown - every "
"list was built and counted. It does not prove the patterns for "
"bigger numbers; the lesson's tree-diagram argument and the Lean "
"proofs do that.",
width=78,
))
if __name__ == "__main__":
main()"""Guessing versus a system: how often does guessing miss an outfit?
There are 4 shirts and 3 pairs of trousers: 12 outfits. A "guesser" names
outfits at random, writes down each new one, and gives up after a few
guesses in a row that bring nothing new ("I can't think of any more").
The program simulates many guessers (with a fixed seed, so every run is the
same) and counts how often they really found all 12. A systematic list
finds all 12 in exactly 12 steps, every time.
Runs unchanged in the browser (Pyodide) using only the standard library.
"""
import random
import textwrap
from fractions import Fraction
OUTFITS = [(s, t) for s in range(4) for t in range(3)]
def guess_until_bored(rng, patience):
"""Guess random outfits; stop after `patience` guesses with nothing new.
Returns the set of different outfits found.
"""
found = set()
misses = 0
while misses < patience and len(found) < len(OUTFITS):
outfit = rng.choice(OUTFITS)
if outfit in found:
misses += 1
else:
found.add(outfit)
misses = 0
return found
def guesses_to_find_all(rng):
"""Number of random guesses until every outfit has appeared."""
found = set()
guesses = 0
while len(found) < len(OUTFITS):
found.add(rng.choice(OUTFITS))
guesses += 1
return guesses
def expected_guesses(n):
"""Exact average number of guesses to see all n outfits: n(1 + ... + 1/n)."""
return n * sum(Fraction(1, k) for k in range(1, n + 1))
def main():
rng = random.Random(2024)
runs = 2000
print(f"{len(OUTFITS)} outfits. A system lists all of them in 12 steps.")
print()
print("Guessers who stop after some guesses in a row with nothing new:")
print(" stop after found all 12 average found")
for patience in (3, 5, 10):
results = [len(guess_until_bored(rng, patience)) for _ in range(runs)]
complete = sum(r == len(OUTFITS) for r in results)
print(f" {patience:>2} misses {100 * complete / runs:>9.1f} %"
f" {sum(results) / runs:>13.2f}")
print()
sims = [guesses_to_find_all(rng) for _ in range(runs)]
exact = expected_guesses(len(OUTFITS))
print("Guessers who never give up need, on average:")
print(f" simulated: {sum(sims) / runs:.2f} guesses"
f" exact: {float(exact):.2f} guesses")
print(f" (the exact value is 12 x (1 + 1/2 + ... + 1/12) = {exact})")
print()
print(textwrap.fill(
f"Epistemic status: the percentages are statistical evidence from "
f"{runs} simulated guessers per row (seeded, so repeatable); another "
"seed gives slightly different numbers. The exact average comes from "
"a short probability argument, not from the simulation. Guessing can "
"never guarantee that nothing is missed; only a system can.",
width=78,
))
if __name__ == "__main__":
main()The random guesser is only a model of someone listing without a system — real people are not dice — but the lesson is clear. Even very patient guessers often miss outfits, and they can never be sure they have them all. A systematic list finds all 12 in 12 steps and proves that there are no more. The percentages are statistical evidence from a simulation; the exact average comes from a probability argument you will meet in later years.
Your turn
Use a list, a table or a tree diagram — whichever you like. You can ask for a hint whenever you like.
1. Systematic listing and counting
Warm-upHumanKwame has 4 shirts and 3 pairs of shorts, all different. How many different outfits (one shirt and one pair of shorts) can he make?
2. Systematic listing and counting
Warm-upHumanFrom Farida's village there are 3 paths down to the river, and from the river 4 roads to the market. A route is one path followed by one road. How many different routes are there from the village to the market?
3. Systematic listing and counting
CoreHumanA canteen offers 2 soups, 3 main dishes and 2 drinks. A meal is one soup, one main dish and one drink. How many different meals are there?
4. Systematic listing and counting
CoreHumanLerato has red, blue and yellow T-shirts and black and brown trousers. She calls out: red–black, blue–brown, red–brown, yellow–black, blue–black. Then she says, "I can't think of any more, so that's all of them." What is the best reply?
5. Systematic listing and counting
CoreHumanUsing the digit cards 1, 5 and 7 (each card at most once), list every two-digit number you can make. Write them separated by commas, like {12, 34}.
6. Systematic listing and counting
CoreHumanMei has 4 different books to put in a row on a shelf. In how many different orders can she arrange them?
7. Systematic listing and counting
CoreHumanSix friends meet, and every two of them shake hands exactly once. How many handshakes are there?
8. Systematic listing and counting
StretchHumanIn a chess club in Nairobi, 8 players each play every other player exactly once. How many games are played?
9. Systematic listing and counting
StretchFormalIn Lean, pairs as bs is the organised list of all pairs from a list as of first choices and a list bs of second choices. Which Lean statement says the product rule?
10. Systematic listing and counting
StretchComputationalComplete outfits(tops, bottoms) so that it returns a list of every pair (top, bottom), each exactly once. Use the system from the lesson: one loop for the tops, and inside it a loop for the bottoms.
Things that trip people up
Misconception
Adding instead of multiplying. 3 T-shirts and 2 pairs of trousers make outfits, not . Adding counts the clothes; multiplying counts the combinations, because every T-shirt goes with every pair of trousers.
Misconception
Counting the same handshake twice. When order does not matter, "Amara–Ben" and "Ben–Amara" are one handshake. Either list each pair once (only pair people with those later in the list), or count both ways and halve.
Misconception
Stopping when no new idea comes to mind. Running out of ideas does not mean running out of possibilities. Use a system, and count: if the product rule says 12, look for the ones you are missing.
Misconception
Thinking order always matters — or never does. In a photo line-up, ABC and CBA are different. In a handshake, or a team of two, Amara-and-Ben is the same as Ben-and-Amara. Always ask: does swapping make a different possibility?
Other ways of seeing
Combinatorics— Counting without listing
Listing works for small problems. For big ones we count without listing, using rules like the product rule. How many 4-digit bicycle-lock codes are there? — far too many to list, easy to count. How many ways can 10 runners finish a race? . Rules like these grow into counting principles and combinatorics, where you will meet permutations, combinations and Pascal's triangle.
Probability— Equally likely possibilities
When every possibility on a systematic list is equally likely — like the 36 results of two dice — counting gives chances: a total of 7 has 6 chances in 36, that is . Much of probability begins with a careful list of what can happen.
Where listing leads
In the language of sets, Lerato's outfits form the Cartesian product of the set of T-shirts and the set of trousers : all ordered pairs with and . The product rule says — that is where the multiplication sign in comes from. Handshakes, where order does not matter, are the 2-element subsets of the set of people.
History and global context
- The ancient Indian medical text Suśruta Saṃhitā counts the ways of combining six basic tastes (sweet, sour, salty, pungent, bitter, astringent): 63 combinations of one or more tastes.
- The Indian scholar Piṅgala, writing about poetry more than two thousand years ago, studied all the patterns of short and long syllables a line of verse can have — an early systematic listing.
- In China, the Yijing (I Ching) uses all 64 figures made of six lines, each line broken or unbroken: .
- In 8th-century Basra (in present-day Iraq), al-Khalīl ibn Aḥmad planned the first Arabic dictionary, Kitāb al-ʿAyn, by systematically listing the possible arrangements of letters in word roots.
For grown-ups: further reading
- George Pólya, How to Solve It (1945) — a classic on problem solving, including organising cases so none is missed.
- Robin Wilson and John J. Watkins (editors), Combinatorics: Ancient and Modern (2013) — the history of counting in many cultures, including India, China and the Islamic world.
- Norman L. Biggs, "The roots of combinatorics", Historia Mathematica 6 (1979) — a short scholarly history.