Monocurl Essays
Series Download

The birthday paradox: why 23 people is enough

In a room of 23 people, two probably share a birthday. The exact formula, why the answer grows like a square root, and what the model assumes.

21 min·High school and early college·

AssumesWhat probability means: counting equally likely outcomesBayes' rule: why a positive test is usually a false alarm

People enter a room one at a time. Each newcomer could share a birthday with anyone already there, so each arrival adds one line for every person present. By the time there are 23 people there are 253 pairs.
Monocurl source
let C = [-1.5, 0, 0]
let R = 1.8
let Web = |m| {
    let k = to_int(floor(m + 0.0001))
    let frac = m - k
    var fresh = []
    if (k < 23 and frac > 0.02) {
        fresh = map(range(0, k), |i| Seg(ppos(k, C, R), lerp(ppos(k, C, R), ppos(i, C, R), frac), EMBER, 2))
    }
    return [
        map(range(1, k), |j| map(range(0, j), |i| Seg(ppos(i, C, R), ppos(j, C, R), tint(AZURE, 0.5), 1.5))),
        fresh,
        map(range(0, 23), |i| Pt(ppos(i, C, R), lerp(MIST, AZURE, clamp(0, (m - i) * 4, 1)), 0.09)),
        center{[2.4, 0.75, 0]} scale{1.7} color{INK} Number(min(k + ceil(frac - 0.02), 23), 0),
        center{[2.4, -0.95, 0]} scale{1.9} color{EMBER} Number(pairs(k) + floor(frac * k + 0.5), 0)
    ]
}
mesh labels = [
    center{[2.4, 1.35, 0]} Cap("people", 0.85),
    center{[2.4, -0.3, 0]} Cap("pairs", 0.85, EMBER)
]
mesh web = Web(m: 1)

slide "arrive"
    play Wait(0.5)
    web.m = 23
    play Lerp(7.5, [&web], linear)
    play Wait(2)

Uses the shared definitions for this lesson's figures.

How many people do you need in a room before it is more likely than not that two of them share a birthday?

Pause and try

Commit to a number before reading on. Then say, in a sentence, what reasoning produced it.

Show answerHide answer

The answer is 23. Most people guess far higher, and the commonest reasoning is “there are 365 days, so you need about half of 365, around 183”. With 183 people a shared birthday is certain for all practical purposes: the chance of none is smaller than one in a million billion billion.

A room of 23 is a school class or two soccer teams and a referee. With that many people, the chance that some two were born on the same day of the year is just over one half. With 50 people it is 97%. People find this so far from their intuition that it is called the birthday paradox, although nothing about it is contradictory. This lesson computes the number exactly, and then explains why it is the size it is.

Whose birthday?

There is a simple calculation that seems to show 23 people are nowhere near enough. Pick one person, say yourself. Each of the other 22 has a 1 in 365 chance of sharing your birthday, so the chance that somebody does is roughly $22/365$, about 6%.

That calculation is nearly right, and it answers a different question. It asks whether somebody shares your birthday. The question in the room is whether any two people share a birthday, and most of those pairs do not involve you.

Two questions about the same 23 people. On the left, the 22 pairs that include you. On the right, every pair in the room: 253 of them. A shared birthday along any one of these lines counts.
Two questions about the same 23 people. On the left, the 22 pairs that include you. On the right, every pair in the room: 253 of them. A shared birthday along any one of these lines counts.
Monocurl source
let CL = [-1.95, 0.4, 0]
let CR = [1.95, 0.4, 0]
let RR = 1.4
let p_you = 1 - (364 / 365) ^ 22
mesh star = [
    map(range(1, 23), |i| Seg(ppos(0, CL, RR), ppos(i, CL, RR), EMBER, 2)),
    map(range(1, 23), |i| Pt(ppos(i, CL, RR), AZURE, 0.075)),
    Pt(ppos(0, CL, RR), EMBER, 0.11)
]
mesh full = [
    map(range(1, 23), |j| map(range(0, j), |i| Seg(ppos(i, CR, RR), ppos(j, CR, RR), tint(AZURE, 0.6), 1.5))),
    map(range(0, 23), |i| Pt(ppos(i, CR, RR), AZURE, 0.075))
]
mesh text = [
    center{[CL[0], -1.35, 0]} Cap("someone shares yours", 0.78, EMBER),
    center{[CL[0], -1.83, 0]} Cap(str_join(["22 pairs: ", pct(p_you)]), 0.78),
    center{[CR[0], -1.35, 0]} Cap("any two share", 0.78, AZURE),
    center{[CR[0], -1.83, 0]} Cap(str_join([to_string(pairs(23), 0), " pairs: ", pct(1 - no_match(23))]), 0.78)
]

Uses the shared definitions for this lesson's figures.

Count the pairs. Each of the 23 people can be paired with 22 others, which gives $23 \times 22 = 506$, but that counts every pair twice (once from each end), so there are $506/2 = 253$ pairs. In general $n$ people form

$$ \binom{n}{2} = \frac{n(n-1)}{2} $$

pairs. The hero animation shows another way to see the same count: the second person to arrive adds 1 pair, the third adds 2, and the $n$th adds $n - 1$, so the total is $1 + 2 + \dots + (n-1)$. The number of pairs grows like the square of the number of people, which is the reason a modest room holds so many chances for a match.

It is tempting to finish at once: 253 pairs, each matching with probability $1/365$, so the chance of a match is $253/365 \approx 0.69$. That overshoots, and we can see it must be wrong in general because with 28 people it gives $378/365$, a probability greater than 1. Adding probabilities is only valid for events that cannot happen together, and two different pairs can both match. We need a calculation that handles the overlaps honestly.

The model

Before computing anything we have to say exactly what is random. We make three assumptions, and we will return to how reasonable they are.

  1. There are 365 possible birthdays. (February 29 is ignored.)
  2. Each person is equally likely to have been born on any of the 365 days.
  3. Different people’s birthdays are independent: knowing one person’s tells you nothing about another’s.

In the language of the first lesson, the sample space for a room of $n$ people is the set of all lists $(b_1, b_2, \dots, b_n)$ in which each $b_i$ is one of the 365 days. There are $365^n$ such lists. Assumptions 2 and 3 together say that all of them are equally likely, for the same reason the 36 outcomes of two dice are: each new person splits every existing case into 365 equal parts. So the probability of an event is the number of lists in it divided by $365^n$.

The event we want, “at least two people share a birthday”, is awkward to count directly, because a list can contain one shared pair, or two, or a triple. Its complement is simple: “all $n$ birthdays are different”. The complement rule, $P(A) = 1 - P(A^c)$, lets us count the simple event and subtract.

Two people, then three

With two people there are $365^2$ equally likely lists. The birthdays differ when the second is any of the 364 days other than the first, so $365 \times 364$ lists have no match, and

$$ P(\text{no match}) = \frac{365 \times 364}{365^2} = \frac{364}{365}, \qquad P(\text{match}) = \frac{1}{365} \approx 0.0027. $$

Pause and try

Do the same for three people. How many of the $365^3$ lists have three different birthdays? What is the probability of at least one match?

Show answerHide answer

The first birthday can be any of 365 days, the second any of the 364 others, and the third any of the 363 days not yet used: $365 \times 364 \times 363$ lists. So $P(\text{no match}) = \frac{364}{365} \times \frac{363}{365} = \frac{132{,}132}{133{,}225} \approx 0.9918$, and $P(\text{match}) \approx 0.0082$. That is almost exactly three times the two-person answer, as it should be: three people make three pairs.

Person by person

The pattern is already visible. Think of the people arriving one at a time, and ask each newcomer to avoid every birthday already in the room. The first person cannot fail. The second must miss 1 day out of 365. The third must miss 2 days, given that the first two were different. When $k$ people with $k$ different birthdays are in the room, the next person avoids them all with probability $(365 - k)/365$.

This is the multiplication rule from the lesson on conditional probability, $P(A \cap B) = P(A \mid B)\,P(B)$, used repeatedly: the chance of no match among the first $k + 1$ people is the chance of no match among the first $k$, times the chance that the newcomer misses all $k$ of their birthdays given that.

  1. Person 1 can have any birthday. All 365 days are safe.
  2. Person 2 must miss 1 taken day: 364 of the 365 days are safe.
  3. Person 3 must miss 2 taken days: 363 safe days.
  4. Keep going. Person 10 must miss 9 days, and the product of the ten factors is about 0.883.
  5. Person 23 must miss 22 days. The product of all 23 factors is about 0.4927, just under one half.
The year as 365 cells, with one cell filled for each person’s birthday in a room where no two people match so far. Each newcomer must land on an empty cell, and the probabilities of doing so multiply.
Monocurl source
let COLS = 27
let CW = 0.2
let day_pos = |d| [-3.45 + mod(d, COLS) * CW, 1.93 - (d // COLS) * CW, 0]
var days = []
var seed = 1
while (len(days) < 23) {
    let d = to_int(floor(D * rnd(seed, 6)))
    if (not (d in days)) {
        days .= d
    }
    seed = seed + 1
}
let DAYS = days
let Taken = |k| map(range(0, k), |i| center{day_pos(DAYS[i])} fill{EMBER} stroke{PAPER, 1.5} Rect([CW, CW]))
let fr = |k| str_join(["\frac{", to_string(D - k), "}{", to_string(D), "}"])
let Chain = |n| {
    if (n <= 3) {
        return str_join(map(range(0, n), |k| fr(k)), " \cdot ")
    }
    return str_join([fr(0), " \cdot ", fr(1), " \cdot ", fr(2), " \cdots ", fr(n - 1)])
}
let Panel = |n, rel| [
    center{[2.85, 1.2, 0]} Cap("people", 0.85),
    center{[2.85, 0.45, 0]} Cap(to_string(n), 1.6, EMBER),
    center{[0, -1.3, 0]} Math(str_join([Chain(n), rel, to_string(no_match(n), 4)]), 1.0),
    center{[0, -1.93, 0]} Cap("chance that no two share a birthday", 0.75, SLATE)
]
mesh calendar = map(range(0, D), |d| center{day_pos(d)} fill{tint(SLATE, 0.16)} stroke{PAPER, 1.5} Rect([CW, CW]))
mesh taken = []
mesh panel = []

slide "Person 1 can have any birthday. All 365 days are safe."
    taken = Taken(1)
    panel = Panel(1, " = ")
    play Fade(0.6)

slide "Person 2 must miss 1 taken day: 364 of the 365 days are safe."
    panel = []
    play Fade(0.3, [&panel])
    taken = Taken(2)
    panel = Panel(2, " \approx ")
    play Fade(0.6)

slide "Person 3 must miss 2 taken days: 363 safe days."
    panel = []
    play Fade(0.3, [&panel])
    taken = Taken(3)
    panel = Panel(3, " \approx ")
    play Fade(0.6)

slide "Keep going. Person 10 must miss 9 days, and the product of the ten factors is about 0.883."
    panel = []
    play Fade(0.3, [&panel])
    taken = Taken(10)
    panel = Panel(10, " \approx ")
    play Fade(0.6)

slide "Person 23 must miss 22 days. The product of all 23 factors is about 0.4927, just under one half."
    panel = []
    play Fade(0.3, [&panel])
    taken = Taken(23)
    panel = Panel(23, " \approx ")
    play Fade(0.6)

Uses the shared definitions for this lesson's figures.

At the last step the calendar still looks almost empty: 23 cells filled out of 365, and the 23rd person had a $343/365 \approx 94\%$ chance of missing them all. No single step is risky. The product falls below one half because there are many steps and each one is a little worse than the one before.

The 23 factors and their running product. Each bar is the chance that one newcomer avoids every birthday already in the room, and none is below 0.93. The orange curve is the product of the bars so far, which is the chance of no match yet, and it crosses one half at the 23rd person.
Monocurl source
let ff = Frame(0, 24, 0, 1.05, [-0.75, 0.05, 0], 5.1, 2.9)
let factor = |j| (D - (j - 1)) / D
let BarAt = |j, col, amount| Shape([at(ff, j - 0.36, 0), at(ff, j + 0.36, 0), at(ff, j + 0.36, factor(j)), at(ff, j - 0.36, factor(j))], col, amount, 1.5)
let Build = |m| {
    let k = to_int(floor(m + 0.0001))
    var line = []
    if (k >= 2) {
        line = Path(map(range(1, k + 1), |j| at(ff, j, no_match(j))), EMBER, 3.5)
    }
    return [
        map(range(1, 24), |j| BarAt(j, lerp(MIST, AZURE, clamp(0, m - j + 1, 1)), 0.3)),
        line,
        map(range(1, k + 1), |j| Pt(at(ff, j, no_match(j)), EMBER, 0.06)),
        center{[3.1, 1.05, 0]} scale{1.4} color{INK} Number(k, 0),
        center{[3.1, -0.05, 0]} scale{1.4} color{AZURE} Number(factor(k), 3),
        center{[3.1, -1.15, 0]} scale{1.4} color{EMBER} Number(no_match(k), 3)
    ]
}
mesh frame = [
    GridLines(ff, [], [0.25, 0.5, 0.75, 1]),
    Axes(ff),
    XTicks(ff, [1, 5, 10, 15, 20, 23]),
    YTicks(ff, [0.5, 1], SLATE, 1),
    center{[fx(ff, 12), fy(ff, 0) - 0.68, 0]} Note("person", 0.72),
    center{[3.1, 1.5, 0]} Cap("person", 0.78),
    center{[3.1, 0.4, 0]} Cap("factor", 0.78, AZURE),
    center{[3.1, -0.7, 0]} Cap("product", 0.78, EMBER)
]
mesh build = Build(m: 1)
mesh half = stroke{LEAF, 2.5} dashed{[0.14, 0.09]} Line(at(ff, 0, 0.5), at(ff, 24, 0.5))

slide "multiply"
    play Wait(0.6)
    build.m = 23
    play Lerp(7, [&build], linear)
    play Wait(1.8)

Uses the shared definitions for this lesson's figures.

The exact formula

The person-by-person argument is correct, and it can be made airtight by going back to counting lists, which avoids any worry about what we are conditioning on.

For $d = 365$ and $n = 23$ the product is $0.4927$, so the chance of a shared birthday is $0.5073$. For $n = 22$ the chance of a shared birthday is $0.4757$. So 23 is the smallest room in which a match is more likely than not.

Pause and try

Before looking at the graph: what do you expect the probability of a shared birthday to be in a room of 50? And how many people are needed for 99%?

Show answerHide answer

For 50 people it is 97.0%. For 99% you need 57 people. Both are far below what the “half of 365” intuition suggests.

The probability that at least two of n people share a birthday, computed from the exact product for every n from 1 to 70. It passes one half at 23 people and 99% at 57.
The probability that at least two of $n$ people share a birthday, computed from the exact product for every $n$ from 1 to 70. It passes one half at 23 people and 99% at 57.
Monocurl source
let fp = Frame(0, 72, 0, 1.05, [-0.1, 0.05, 0], 6.3, 2.8)
let hit = |n| 1 - no_match(n)
let need99 = block {
    var n = 1
    while (hit(n) < 0.99) {
        n = n + 1
    }
    . n
}[0]
mesh frame = [
    GridLines(fp, [10, 20, 30, 40, 50, 60, 70], [0.25, 0.5, 0.75, 1]),
    Axes(fp),
    XTicks(fp, [10, 20, 30, 40, 50, 60, 70]),
    YTicks(fp, [0.25, 0.5, 0.75, 1], SLATE, 2),
    center{[fx(fp, 36), fy(fp, 0) - 0.68, 0]} Note("people in the room", 0.72),
    center{[fx(fp, 0) + 1.0, fy(fp, 1.05) + 0.45, 0]} Note("chance of a shared birthday", 0.72)
]
mesh guide = [
    Seg(at(fp, 23, 0), at(fp, 23, hit(23)), LEAF, 2.5),
    Seg(at(fp, 0, hit(23)), at(fp, 23, hit(23)), LEAF, 2.5)
]
mesh curve = [
    Path(map(range(1, 71), |n| at(fp, n, hit(n))), AZURE, 3),
    map(range(1, 71), |n| Pt(at(fp, n, hit(n)), AZURE, 0.04))
]
mesh marks = [
    Ring(at(fp, 23, hit(23)), LEAF, 0.1, 3.5),
    Ring([-0.75, fy(fp, 1.05) + 0.45, 0], LEAF, 0.1, 3.5),
    center{[0.17, fy(fp, 1.05) + 0.45, 0]} Cap(str_join(["23 people: ", pct(hit(23))]), 0.75, LEAF),
    Ring(at(fp, need99, hit(need99)), PLUM, 0.1, 3.5),
    Ring([1.3, fy(fp, 1.05) + 0.45, 0], PLUM, 0.1, 3.5),
    center{[2.22, fy(fp, 1.05) + 0.45, 0]} Cap(str_join([to_string(need99), " people: ", pct(hit(need99))]), 0.75, PLUM)
]

Uses the shared definitions for this lesson's figures.

Asking a thousand rooms

A probability of 0.507 is a claim about frequency: of many rooms of 23 people, about 50.7% should contain a shared birthday. We can check it by simulation. The figure fills 1000 rooms with 23 random birthdays each and marks the rooms in which two coincide.

A simulation of 1000 rooms, each with 23 people whose birthdays are drawn independently and uniformly from 365 days. A room is colored orange if two of its people share a birthday. The fraction of orange rooms settles near the computed 0.507.
Monocurl source
let ROOMS = 1000
var hit_acc = []
var cum_acc = [0]
var c = 0
for (r in range(0, ROOMS)) {
    var seen = []
    var h = 0
    for (i in range(0, 23)) {
        let d = to_int(floor(D * rnd(r * 23 + i + 1, 1)))
        if (d in seen) {
            h = 1
        }
        seen .= d
    }
    c = c + h
    hit_acc .= h
    cum_acc .= c
}
let HIT = hit_acc
let CUMH = cum_acc
let RW = 0.13
let room_pos = |r| [-3.185 + mod(r, 50) * RW, 1.9 - (r // 50) * RW, 0]
let Room = |r| {
    var col = tint(AZURE, 0.45)
    if (HIT[r] == 1) {
        col = EMBER
    }
    return center{room_pos(r)} fill{col} stroke{PAPER, 1.5} Rect([RW, RW])
}
let Fill = |j| {
    let k = to_int(floor(j))
    return [
        map(range(0, k), |r| Room(r)),
        center{[-2.3, -1.75, 0]} scale{1.4} color{INK} Number(k, 0),
        center{[0, -1.75, 0]} scale{1.4} color{EMBER} Number(CUMH[k], 0),
        center{[2.3, -1.75, 0]} scale{1.4} color{INK} Number(CUMH[k] / max(k, 1), 3)
    ]
}
mesh empty = map(range(0, ROOMS), |r| center{room_pos(r)} fill{tint(SLATE, 0.12)} stroke{PAPER, 1.5} Rect([RW, RW]))
mesh labels = [
    center{[-2.3, -1.2, 0]} Cap("simulated rooms", 0.78),
    center{[0, -1.2, 0]} Cap("with a match", 0.78, EMBER),
    center{[2.3, -1.2, 0]} Cap("fraction", 0.78)
]
mesh fill_in = Fill(j: 10)

slide "fill"
    play Wait(0.5)
    fill_in.j = ROOMS
    play Lerp(7, [&fill_in], linear)
    play Wait(1.8)

Uses the shared definitions for this lesson's figures.

In this run 516 of the 1000 rooms had a match. The model predicts 507 on average, and a count of 1000 yes-or-no trials typically lands within 15 or 20 of its average, so the simulation and the formula agree.

Why the answer is about a square root

The exact product settles the question for 365 days, but it does not explain the size of the answer. Why 23, and not 10 or 60? What would the number be for a year of 1000 days? A product of 23 fractions is hard to reason about. A sum would be easier, and exponentials turn products into sums, since $e^{a} e^{b} = e^{a+b}$. So we look for a way to compare each factor $1 - \frac{k}{365}$ with an exponential.

The line 1 - x and the curve e^{-x}. The line is tangent to the curve at (0, 1) and lies below it everywhere else. Every factor in the birthday product for 23 people has x between 0 and 22/365, inside the narrow shaded band where the two are nearly equal.
The line $1 - x$ and the curve $e^{-x}$. The line is tangent to the curve at $(0, 1)$ and lies below it everywhere else. Every factor in the birthday product for 23 people has $x$ between 0 and $22/365$, inside the narrow shaded band where the two are nearly equal.
Monocurl source
let fe = Frame(-1, 2.6, -1.6, 2.8, [0.05, 0.12, 0], 6.0, 3.5)
let EdgeTicks = |xs, ys| block {
    for (x in xs) {
        . center{[fx(fe, x), fy(fe, -1.6) - 0.3, 0]} Math(to_string(x), 0.72, SLATE)
    }
    for (y in ys) {
        . center{[fx(fe, -1) - 0.36, fy(fe, y), 0]} Math(to_string(y), 0.72, SLATE)
    }
}
mesh band = Region([at(fe, 0, -1.6), at(fe, 22 / D, -1.6), at(fe, 22 / D, 2.8), at(fe, 0, 2.8)], tint(PLUM, 0.35))
mesh frame = [
    GridLines(fe, [-1, 1, 2], [-1, 1, 2]),
    Outline([at(fe, -1, -1.6), at(fe, 2.6, -1.6), at(fe, 2.6, 2.8), at(fe, -1, 2.8)], MIST, 1.5),
    Axes(fe, "x", ""),
    EdgeTicks([-1, 0, 1, 2], [-1, 0, 1, 2])
]
mesh graphs = [
    Seg(at(fe, -1, 2), at(fe, 2.6, -1.6), EMBER, 4),
    Graph(fe, |x| exp(0 - x), -1, 2.6, AZURE, 4),
    Pt(at(fe, 0, 1), LEAF, 0.1)
]
mesh labels = [
    center{at(fe, 2.0, 0.55)} Math("e^{-x}", 1.0, AZURE),
    center{at(fe, 0.5, -0.5)} Math("1 - x", 1.0, EMBER),
    center{at(fe, 0.56, 1.55)} Cap("birthday factors", 0.72, PLUM)
]

Uses the shared definitions for this lesson's figures.

Apply this to each factor of the product, with $x = k/d$.

Look at the exponent. It is $\binom{n}{2}/d$: the number of pairs divided by the number of days. The idea we started with, that pairs are what matter, has reappeared inside a proof, in a form that is correct. The naive estimate “pairs over days” was the right quantity; it belongs in an exponent.

Now the size of the answer can be read off. The bound guarantees a match with probability at least $\frac12$ as soon as $e^{-\binom{n}{2}/d} \le \frac12$, that is, as soon as

$$ \binom{n}{2} \ge d \ln 2. $$

For $d = 365$ the right side is $365 \ln 2 \approx 252.9987$, and 23 people form exactly 253 pairs. So the bound alone proves that 23 people suffice, with almost nothing to spare. For general $d$, the condition $n(n-1) \ge 2d\ln 2$ holds once $n$ is a little more than $\sqrt{2 \ln 2}\,\sqrt{d} \approx 1.18\sqrt{d}$. The number of people needed grows like the square root of the number of days, because the number of pairs grows like the square of the number of people.

The square-root law. Each dot is the exact number of people needed for a better than even chance of a match when there are d equally likely days, computed from the product formula. The curve is \frac12 + \sqrt{\frac14 + 2d\ln 2}, the point where the bound guarantees one half.
The square-root law. Each dot is the exact number of people needed for a better than even chance of a match when there are $d$ equally likely days, computed from the product formula. The curve is $\frac12 + \sqrt{\frac14 + 2d\ln 2}$, the point where the bound guarantees one half.
Monocurl source
let fd = Frame(0, 1050, 0, 42, [-0.1, 0.1, 0], 6.3, 2.9)
let need = |d| {
    var n = 1
    var p = 1
    while (p > 0.5) {
        p = p * (d - n) / d
        n = n + 1
    }
    return n
}
let bound = |d| 0.5 + sqrt(0.25 + 2 * d * ln(2))
mesh frame = [
    GridLines(fd, [250, 500, 750, 1000], [10, 20, 30, 40]),
    Axes(fd),
    XTicks(fd, [250, 500, 750, 1000]),
    YTicks(fd, [10, 20, 30, 40]),
    center{[fx(fd, 525), fy(fd, 0) - 0.68, 0]} Note("number of equally likely days, d", 0.72),
    center{[fx(fd, 0) + 1.7, fy(fd, 42) + 0.42, 0]} Note("people needed for a 50%% chance", 0.72)
]
mesh curve = Graph(fd, bound, 0, 1040, GOLD, 3.5)
mesh dots = map(range(25, 1001, 25), |d| Pt(at(fd, d, need(d)), AZURE, 0.055))
mesh marks = [
    Ring(at(fd, D, need(D)), LEAF, 0.11, 3.5),
    center{at(fd, 520, 16)} Cap(str_join([to_string(D), " days: ", to_string(need(D)), " people"]), 0.78, LEAF),
    center{at(fd, 760, 39.5)} Math("\approx 1.18\sqrt{d}", 0.95, GOLD)
]

Uses the shared definitions for this lesson's figures.

The bound is a one-way guarantee: it says $1.18\sqrt{d}$ people are enough. That you cannot do with far fewer follows from a bound in the other direction, $P(\text{match}) \le \binom{n}{2}/d$, which is the last problem below.

How good are the assumptions?

The three assumptions are a model of real birthdays, and each is only approximately true.

365 days. Including February 29 as a 366th equally likely day (which overstates it) changes the probability for 23 people from 0.5073 to 0.5063. The threshold stays at 23.

Equally likely days. Real births are not uniform over the year; in many countries late summer and early autumn are busier, and weekends and holidays quieter. This makes a match more likely. To see why that is plausible, push it to the extreme: if everyone were born in the same month, a room of 23 would almost certainly contain a match. In fact, for any fixed $n$ the uniform distribution gives the smallest possible chance of a match, so our figures are lower bounds for real populations. That is a standard result, and here is a sketch of the reason. Let day $i$ have probability $p_i$, single out two days, and sort the lists with no match by whether they use both of those days, exactly one, or neither. Adding up the three kinds gives $P(\text{no match}) = A\,p_1 p_2 + B\,(p_1 + p_2) + C$, where $A, B, C \ge 0$ depend only on the other days. Now replace $p_1$ and $p_2$ by their average. The sum $p_1 + p_2$ is unchanged, and the product does not decrease, because $\left(\frac{p_1 + p_2}{2}\right)^2 - p_1 p_2 = \left(\frac{p_1 - p_2}{2}\right)^2 \ge 0$. So evening out two days never raises the chance of a match, and repeatedly evening out the most and least likely days brings any distribution as close to uniform as we like. (The sketch leaves out the limiting step at the end, which uses the fact that the formula is continuous in the $p_i$.) For realistic birth data the effect is small, and the threshold stays at 23.

Independence. This fails in a room containing twins, and it fails if people were invited because of their birthdays. It holds well for a group assembled for unrelated reasons.

So the conclusion is robust: the departures from the model are small, and the main one pushes in the direction of more matches.

Worked examples

Common mistakes

Stepping back

What made this problem hard was looking at the wrong objects. A room of 23 feels small against 365 days because we compare people with days. The comparison that matters is pairs with days, and 253 pairs against 365 days is no longer lopsided. The exact product confirmed the number, and the inequality $1 - x \le e^{-x}$ explained it by turning the product into $e^{-\text{pairs}/\text{days}}$.

The square-root law reaches well beyond birthdays. Whenever items are assigned at random to $d$ slots, repeats begin when the number of items is around $\sqrt{d}$, far sooner than when the slots fill up. The technique carries over too: compute the complement, multiply conditional probabilities along a chain, and bound a product by an exponential. The next lesson returns to the 253 pairs and finds the average number of them that match.

Check yourself

In a class of 30 students (with the lesson’s assumptions), which statement is correct?

Problems

Use a calculator for the products. The first three are direct, the next three combine ideas, and the last two ask for arguments.

Problem 1

Find the probability that at least two of four people share a birthday. Compare it with the number of pairs divided by 365.

Show solutionHide solution

$P(\text{no match}) = \frac{364}{365} \cdot \frac{363}{365} \cdot \frac{362}{365} = \frac{47{,}831{,}784}{48{,}627{,}125} \approx 0.98364$, so $P(\text{match}) \approx 0.0164$. Four people make $\binom{4}{2} = 6$ pairs, and $6/365 \approx 0.0164$. The two agree to three significant figures because with so few people overlapping matches are very rare.

Problem 2

How many pairs are there among 30 people? What is the smallest group in which the number of pairs exceeds 365?

Show solutionHide solution

$\binom{30}{2} = \frac{30 \times 29}{2} = 435$. For the second part we need $n(n-1)/2 > 365$, that is, $n(n-1) > 730$. Since $27 \times 26 = 702$ and $28 \times 27 = 756$, the smallest such group has 28 people, with 378 pairs.

Problem 3

Assume each person is equally likely to be born in any of the 12 months, independently. How many people are needed before two of them probably share a birth month?

Show solutionHide solution

Use the product formula with $d = 12$. For $n = 4$: $P(\text{no match}) = \frac{11}{12} \cdot \frac{10}{12} \cdot \frac{9}{12} = \frac{990}{1728} \approx 0.573$, so a match has probability $0.427$. For $n = 5$: multiply by $\frac{8}{12}$ to get $\frac{7920}{20{,}736} \approx 0.382$, so a match has probability $0.618$. Five people are needed. The rule of thumb gives $1.18\sqrt{12} \approx 4.1$, which points to the same answer once rounded up to the next whole number that works.

Problem 4

In a class of 30, find (a) the probability that at least one of your 29 classmates shares your birthday, and (b) the probability that some two students in the class share a birthday. Explain the size of the gap.

Show solutionHide solution

(a) Each classmate misses your birthday with probability $364/365$, independently, so $P = 1 - (364/365)^{29} \approx 1 - 0.9235 = 0.0765$. (b) $P = 1 - \prod_{k=0}^{29}(1 - k/365) \approx 1 - 0.2937 = 0.7063$. Event (a) concerns the 29 pairs that include you; event (b) concerns all 435 pairs, fifteen times as many.

Problem 5

Use the bound $P(\text{match}) \ge 1 - e^{-n(n-1)/730}$ to find a number of people that guarantees a 99% chance of a shared birthday. The exact threshold is 57. Why does the bound give a larger number?

Show solutionHide solution

We need $e^{-n(n-1)/730} \le 0.01$, that is, $n(n-1) \ge 730 \ln 100 \approx 3361.8$. Since $58 \times 57 = 3306$ and $59 \times 58 = 3422$, the bound guarantees 99% for $n = 59$. It is larger than 57 because $e^{-x}$ is strictly greater than $1 - x$ for $x > 0$, so the exponential overestimates the chance of no match, and the overestimate is worse for the later factors, where $x = k/365$ is as large as $0.16$. The bound is always on the safe side: any $n$ it certifies really does work.

Problem 6

Redo the 23-person calculation with 366 equally likely days. Does the threshold change?

Show solutionHide solution

With $d = 366$, $P(\text{no match}) = \prod_{k=0}^{22}(1 - k/366) \approx 0.4937$, so $P(\text{match}) \approx 0.5063$, still above one half. For 22 people the match probability is about $0.4748$, still below. The threshold remains 23. One extra day in 365 changes each factor by about one part in a thousand, too little to move the crossing point by a whole person.

Problem 7

A student argues: “There are 253 pairs among 23 people. Each pair fails to match with probability $364/365$. So the chance that no pair matches is $(364/365)^{253} \approx 0.4995$, and the chance of a match is $0.5005$.” The answer is close to the true value $0.5073$, but the argument contains an unjustified step. Find it.

Show solutionHide solution

Multiplying the 253 probabilities assumes that the 253 events “pair $\{i, j\}$ does not match” are independent of one another, and they are not. Take three people, A, B and C. If A and B match and B and C match, then A and C are certain to match; the third event is completely determined by the other two. So the events for different pairs are linked whenever the pairs share people. (It is true that any two of these events are independent, which is part of why the error is small, but independence two at a time gives no license to multiply 253 of them.) The exact answer comes from the product $\prod (1 - k/365)$, whose factors are conditional probabilities along a chain, each given everything before it. The student’s number is a good approximation and a wrong derivation.

Problem 8

(a) Using the fact that $P(A \cup B) \le P(A) + P(B)$ for any two events, show that the probability of a shared birthday among $n$ people is at most $\binom{n}{2}/365$. (b) Deduce that no room of fewer than 20 people can have a chance of one half or more. (c) Combine this with the lesson’s bound to describe how the threshold must grow with $d$.

Show solutionHide solution

(a) For each pair $\{i, j\}$ let $M_{ij}$ be the event that persons $i$ and $j$ share a birthday. Whatever $b_i$ is, $b_j$ equals it with probability $1/365$, so $P(M_{ij}) = 1/365$. The event “some two share a birthday” is the union of all $\binom{n}{2}$ events $M_{ij}$. Applying $P(A \cup B) \le P(A) + P(B)$ repeatedly, one event at a time, the probability of a union of finitely many events is at most the sum of their probabilities. Hence $P(\text{match}) \le \binom{n}{2} \cdot \frac{1}{365}$.

(b) For $n = 19$ there are 171 pairs and the bound gives $P(\text{match}) \le 171/365 \approx 0.468 < \frac12$. Fewer people give fewer pairs. (For $n = 20$ the bound is $190/365 > \frac12$ and says nothing.) So the threshold is at least 20; the lesson showed it is at most 23.

(c) For $d$ days the same argument gives $P(\text{match}) \le \binom{n}{2}/d$, so a chance of one half requires $\binom{n}{2} \ge d/2$, roughly $n \ge \sqrt{d}$. The lesson’s bound shows that $\binom{n}{2} \ge d\ln 2$, roughly $n \ge 1.18\sqrt{d}$, is enough. So the threshold lies between about $\sqrt{d}$ and $1.18\sqrt{d} + 1$: it is of the order of $\sqrt{d}$, from both sides.

Shared definitions used by this lesson's figures
# house kit: prepended to every essay figure after the std imports

# palette. AZURE and EMBER are the two actors, LEAF marks an answer, ROSE a problem
let PAPER = [1, 1, 1, 1]
let INK = [0.08, 0.09, 0.11, 1]
let SLATE = [0.40, 0.44, 0.50, 1]
let MIST = [0.85, 0.86, 0.88, 1]
let AZURE = [0.13, 0.42, 0.68, 1]
let EMBER = [0.89, 0.42, 0.12, 1]
let LEAF = [0.13, 0.55, 0.36, 1]
let ROSE = [0.78, 0.22, 0.24, 1]
let PLUM = [0.47, 0.30, 0.66, 1]
let GOLD = [0.85, 0.62, 0.08, 1]

background = PAPER

# an opaque pastel of col; use for fills so overlaps do not darken
let tint = |col, amount = 0.18| [
    1 - (1 - col[0]) * amount,
    1 - (1 - col[1]) * amount,
    1 - (1 - col[2]) * amount,
    1
]

# text: Cap for labels, Note for quiet labels, Math for typeset maths
let Cap = |txt, size = 0.75, col = INK| color{col} Text(txt, size, "Charter")
let Note = |txt, size = 0.65| color{SLATE} Text(txt, size, "Charter")
let Math = |tex, size = 0.9, col = INK| color{col} Tex(tex, size)

# a paper rectangle to sit behind text that crosses lines
let Plate = |pos, size| center{pos} fill{PAPER} stroke{alpha{0} PAPER, 0} Rect(size)

# shapes with fill and stroke both set, so no stray black appears
let Pt = |pos, col = INK, r = 0.075| center{pos} fill{col} stroke{col, 1} Circle(r, 48)
let Ring = |pos, col = INK, r = 0.1, w = 3| center{pos} fill{PAPER} stroke{col, w} Circle(r, 48)
let Seg = |a, b, col = INK, w = 3| stroke{col, w} Line(a, b)
let Path = |pts, col = INK, w = 3| stroke{col, w} Polyline(pts)
let Region = |pts, col| fill{col} stroke{alpha{0} col, 0} Polygon(pts)
let Outline = |pts, col = INK, w = 3| fill{alpha{0} PAPER} stroke{col, w} Polygon(pts)
let Shape = |pts, col, amount = 0.18, w = 3| fill{tint(col, amount)} stroke{col, w} Polygon(pts)
let Disc = |pos, r, col, amount = 0.18, w = 3| center{pos} fill{tint(col, amount)} stroke{col, w} Circle(r, 96)
let Box = |pos, size, col, amount = 0.18, w = 3| center{pos} fill{tint(col, amount)} stroke{col, w} Rect(size)
# a line from a to b ending in a small solid arrowhead
let Tip = |a, b, col = INK, w = 3, head = 0.17| {
    let d = normalize([b[0] - a[0], b[1] - a[1], 0])
    let n = [-d[1], d[0], 0]
    let base = [b[0] - d[0] * head, b[1] - d[1] * head, 0]
    return [
        stroke{col, w} Line(a, base),
        fill{col} stroke{col, 1} Polygon([
            b,
            [base[0] + n[0] * head * 0.42, base[1] + n[1] * head * 0.42, 0],
            [base[0] - n[0] * head * 0.42, base[1] - n[1] * head * 0.42, 0]
        ])
    ]
}

# plots. a frame is [x0, x1, y0, y1, centre, width, height]: a data window and the scene box it fills
let Frame = |x0, x1, y0, y1, c = [0, 0, 0], w = 6.4, h = 3.6| [x0, x1, y0, y1, c, w, h]
let fx = |fr, x| fr[4][0] + (x - (fr[0] + fr[1]) / 2) * fr[5] / (fr[1] - fr[0])
let fy = |fr, y| fr[4][1] + (y - (fr[2] + fr[3]) / 2) * fr[6] / (fr[3] - fr[2])
let at = |fr, x, y| [fx(fr, x), fy(fr, y), 0]

# axes through the data origin (or the nearest edge of the window), with optional names
let Axes = |fr, x_name = "", y_name = "", col = SLATE| {
    let ox = clamp(fr[0], 0, fr[1])
    let oy = clamp(fr[2], 0, fr[3])
    var parts = [
        Tip(at(fr, fr[0], oy), [fx(fr, fr[1]) + 0.25, fy(fr, oy), 0], col, 2.5, 0.15),
        Tip(at(fr, ox, fr[2]), [fx(fr, ox), fy(fr, fr[3]) + 0.25, 0], col, 2.5, 0.15)
    ]
    if (x_name != "") {
        parts .= center{[fx(fr, fr[1]) + 0.5, fy(fr, oy), 0]} Math(x_name, 0.8, col)
    }
    if (y_name != "") {
        parts .= center{[fx(fr, ox), fy(fr, fr[3]) + 0.5, 0]} Math(y_name, 0.8, col)
    }
    return parts
}

# tick marks with numbers at the listed data values
let XTicks = |fr, xs, col = SLATE, places = 0| block {
    let oy = fy(fr, clamp(fr[2], 0, fr[3]))
    for (x in xs) {
        . Seg([fx(fr, x), oy - 0.07, 0], [fx(fr, x), oy + 0.07, 0], col, 2)
        . center{[fx(fr, x), oy - 0.33, 0]} Math(to_string(x, places), 0.72, col)
    }
}
let YTicks = |fr, ys, col = SLATE, places = 0| block {
    let ox = fx(fr, clamp(fr[0], 0, fr[1]))
    for (y in ys) {
        . Seg([ox - 0.07, fy(fr, y), 0], [ox + 0.07, fy(fr, y), 0], col, 2)
        . center{[ox - 0.36, fy(fr, y), 0]} Math(to_string(y, places), 0.72, col)
    }
}

# faint grid lines at the listed data values
let GridLines = |fr, xs, ys, col = MIST| block {
    for (x in xs) {
        . Seg(at(fr, x, fr[2]), at(fr, x, fr[3]), col, 1.5)
    }
    for (y in ys) {
        . Seg(at(fr, fr[0], y), at(fr, fr[1], y), col, 1.5)
    }
}

# the graph of f over [a, b], drawn in the frame. values are clamped to the window
let Graph = |fr, f, a, b, col = AZURE, w = 4, n = 160|
    stroke{col, w} Polyline(map(sample(a, b, n), |x| at(fr, x, clamp(fr[2], f(x), fr[3]))))

# the region between the graph of f and the x axis over [a, b]
let AreaUnder = |fr, f, a, b, col = AZURE, amount = 0.2, n = 80| {
    var pts = [at(fr, a, 0)]
    for (x in sample(a, b, n)) {
        pts .= at(fr, x, clamp(fr[2], f(x), fr[3]))
    }
    pts .= at(fr, b, 0)
    return fill{tint(col, amount)} stroke{alpha{0} col, 0} Polygon(pts)
}

let D = 365
let rnd = |i, salt| mod(seeded_random(i, salt) + 1, 1)
# chance that n people have n different birthdays, with d equally likely days
let no_match = |n, d = D| {
    var p = 1
    for (k in range(0, n)) {
        p = p * (d - k) / d
    }
    return p
}
let pairs = |n| n * (n - 1) / 2
# person i of n, standing on a circle
let ppos = |i, c, r, n = 23| [c[0] + r * cos(PI / 2 - TAU * i / n), c[1] + r * sin(PI / 2 - TAU * i / n), 0]
let pct = |x, places = 1| str_join([to_string(100 * x, places), "%%"])