Monocurl Essays
Series Download

Expected value and the surprising power of adding averages

The expected value of a random quantity is its long-run average. Expected values add even for dependent quantities, which solves the hat-check problem.

23 min·High school and early college·

AssumesWhat probability means: counting equally likely outcomes

A simulation of 2000 plays of a carnival game that costs $2. The curve is the average payout per play so far. It jumps around during the first plays and then stays near $1.50, below the price.
Monocurl source
let NP = 2000
var acc = [0]
var tot = 0
for (i in range(1, NP + 1)) {
    tot = tot + pay(roll(i, 3))
    acc .= tot
}
let ACC = acc
var idx = []
for (k in range(1, 41)) {
    idx .= k
}
for (k in range(50, NP + 1, 10)) {
    idx .= k
}
let IDX = idx
let fs = Frame(0, NP, 0, 3, [-0.95, 0.05, 0], 4.7, 3.0)
let Run = |j| {
    let k = to_int(floor(j))
    let n = IDX[k]
    let pts = filter(map(range(0, k + 1), |m| [IDX[m], ACC[IDX[m]] / IDX[m]]), |q| q[1] <= 3)
    return [
        Path(map(pts, |q| at(fs, q[0], q[1])), AZURE, 3),
        Pt(at(fs, n, min(ACC[n] / n, 3)), AZURE, 0.075),
        center{[3.0, 0.75, 0]} scale{1.3} color{INK} Number(n, 0),
        center{[3.0, -0.75, 0]} scale{1.3} color{AZURE} Number(ACC[n] / n, 2)
    ]
}
mesh frame = [
    GridLines(fs, [500, 1000, 1500, 2000], [1, 2, 3]),
    Axes(fs),
    XTicks(fs, [500, 1000, 1500, 2000]),
    YTicks(fs, [1, 2, 3]),
    center{[fx(fs, 1000), fy(fs, 0) - 0.68, 0]} Note("plays", 0.72),
    center{[fx(fs, 0) + 1.45, fy(fs, 3) + 0.42, 0]} Note("average payout (dollars)", 0.72),
    center{[3.0, 1.75, 0]} Cap("simulation", 0.75, SLATE),
    center{[3.0, 1.2, 0]} Cap("plays", 0.78, INK),
    center{[3.0, -0.3, 0]} Cap("average payout", 0.78, AZURE)
]
mesh lines = [
    stroke{ROSE, 2.5} dashed{[0.14, 0.09]} Line(at(fs, 0, PRICE), at(fs, NP, PRICE)),
    center{at(fs, 1650, PRICE) + [0, 0.24, 0]} Cap("price 2.00", 0.72, ROSE),
    stroke{LEAF, 2.5} dashed{[0.14, 0.09]} Line(at(fs, 0, EV), at(fs, NP, EV)),
    center{at(fs, 1650, EV) + [0, -0.26, 0]} Cap(str_join(["expected ", to_string(EV, 2)]), 0.72, LEAF)
]
mesh run = Run(j: 1)

slide "run"
    play Wait(0.5)
    run.j = len(IDX) - 1
    play Lerp(7, [&run], linear)
    play Wait(1.8)

Uses the shared definitions for this lesson's figures.

A carnival stall offers this game. You pay $2 and roll one fair die. A 6 pays you $5, a 4 or a 5 pays you $2, and a 1, 2 or 3 pays nothing. Half the faces give you at least your money back, and one of them more than doubles it. Is the game worth playing?

One play cannot answer that. You might roll a 6 and walk away $3 richer, or roll a 2 and lose your $2, and neither result says whether the price was fair. The question has a definite answer only when it is about many plays: if you played all afternoon, would the payouts keep up with the $2 you hand over each time?

The animation above simulates 2000 plays and tracks the average payout per play. After a noisy start it stays close to $1.50. The stall takes in $2.00 per play and hands back about $1.50, so over a long afternoon you lose about fifty cents a play. This lesson is about that number: how to compute it without simulating anything, what exactly it is, and one property of it that makes some hard-looking problems short.

The long-run average is a weighted average

Here are the first 600 simulated plays, one dot per play.

  1. Each dot is one play, in the order they happened. Grey pays nothing, blue pays 2 dollars, orange pays 5.
  2. Sort the plays by payout. The three groups are close to one half, one third and one sixth of the 600.
  3. Total payout is 0 times the grey count, plus 2 times the blue count, plus 5 times the orange count. Divide by 600 for the average.
Six hundred simulated plays of the carnival game, one dot per play, colored by payout. Sorting the dots by payout turns the total into three products, and dividing by 600 gives the average payout per play.
Monocurl source
let NPL = 600
let COLS = 40
let GAP = 0.17
let home = |i| [-3.31 + mod(i, COLS) * GAP, 1.9 - (i // COLS) * GAP, 0]
let PAYS = map(range(0, NPL), |i| pay(roll(i + 1, 3)))
let VALS = [0, 2, 5]
let CNT = map(VALS, |v| count(PAYS, |q| q == v))
var rank_acc = map(range(0, NPL), |i| 0)
var next = 0
for (v in VALS) {
    for (i in range(0, NPL)) {
        if (PAYS[i] == v) {
            rank_acc[i] = next
            next = next + 1
        }
    }
}
let RANK = rank_acc
let TOTAL = sum(PAYS)
let Dots = |s| map(range(0, NPL), |i| center{lerp(home(i), home(RANK[i]), s)} fill{pay_col(PAYS[i])} stroke{pay_col(PAYS[i]), 1} Circle(0.06, 12))
let Key = |x, v, txt| [
    Pt([x - 0.75, -0.95, 0], pay_col(v), 0.09),
    center{[x, -0.95, 0]} Cap(txt, 0.78, INK)
]
mesh dots = Dots(s: 0)
mesh legend = []
mesh counts = []
mesh result = []

slide "Each dot is one play, in the order they happened. Grey pays nothing, blue pays 2 dollars, orange pays 5."
    legend = [Key(-2.2, 0, "pays 0"), Key(0.1, 2, "pays 2"), Key(2.4, 5, "pays 5"), center{[0, -1.65, 0]} Note("simulation: 600 plays", 0.74)]
    play Fade(0.7)

slide "Sort the plays by payout. The three groups are close to one half, one third and one sixth of the 600."
    legend = []
    play Fade(0.3, [&legend])
    dots.s = 1
    play Lerp(2.2, [&dots])
    counts = map(range(0, 3), |g| center{[-2.3 + 2.3 * g, -0.95, 0]} Cap(str_join([to_string(CNT[g]), " plays pay ", to_string(VALS[g])]), 0.78, [INK, AZURE, EMBER][g]))
    play Fade(0.5, [&counts])

slide "Total payout is 0 times the grey count, plus 2 times the blue count, plus 5 times the orange count. Divide by 600 for the average."
    result = center{[0, -1.65, 0]} Math(str_join([
        "\frac{0 \cdot ", to_string(CNT[0]), " + 2 \cdot ", to_string(CNT[1]), " + 5 \cdot ", to_string(CNT[2]), "}{", to_string(NPL), "} = \frac{", to_string(TOTAL), "}{", to_string(NPL), "} \approx ", to_string(TOTAL / NPL, 3)
    ]), 0.95)
    play Fade(0.6, [&result])

Uses the shared definitions for this lesson's figures.

The third step is the one to study. After sorting, the total of the 600 payouts is three products, one for each possible payout. Dividing by 600 turns each count into a fraction of the plays:

$$ \text{average payout} = 0 \times \frac{\#\{\text{pays } 0\}}{600} + 2 \times \frac{\#\{\text{pays } 2\}}{600} + 5 \times \frac{\#\{\text{pays } 5\}}{600}. $$

Now recall what probability means as a long-run frequency, from the lesson on sample spaces: over many rolls, the fraction that land on a given set of faces settles near the probability of that set. Three of the six faces pay nothing, two pay $2 and one pays $5, so the three fractions settle near $\frac36$, $\frac26$ and $\frac16$. The average payout therefore settles near

$$ 0 \times \frac36 + 2 \times \frac26 + 5 \times \frac16 = \frac{0 + 4 + 5}{6} = 1.50 \text{ dollars}. $$

That is the level the simulation found, and we got it with no dice at all. Each possible payout is weighted by its probability and the results are added. The game costs $2.00 and returns $1.50 on average, so it is not worth playing if money is the point.

Pause and try

A different stall charges $1 to roll one die and pays you, in dollars, a quarter of the number you roll (so a 6 pays $1.50). Compute the long-run average payout. Is this game better or worse for the player than the first?

Show answerHide answer

Each face has probability $\frac16$, so the average payout is $\frac14 \cdot \frac{1 + 2 + 3 + 4 + 5 + 6}{6} = \frac14 \cdot 3.5 = 0.875$ dollars. You lose 12.5 cents per $1 play on average, or 12.5% of the stake, against 25% of the stake in the first game. The second game is better for the player and still a losing one.

Random variables and expectation

To state this precisely we need a name for “a number that depends on the outcome of a random experiment”. The payout is one. The experiment is the roll, the sample space is $\Omega = \{1, 2, 3, 4, 5, 6\}$, and the payout assigns a number to each outcome: 0 to the outcomes 1, 2, 3; 2 to the outcomes 4, 5; and 5 to the outcome 6. An assignment of a number to each outcome is a function on $\Omega$, and that is the definition.

The name “random variable” is traditional and a little misleading, because $X$ itself is a fixed function with nothing random about it. The randomness is in which outcome $\omega$ occurs. For the carnival game the definition gives

$$ E[X] = 0 \cdot \tfrac16 + 0 \cdot \tfrac16 + 0 \cdot \tfrac16 + 2 \cdot \tfrac16 + 2 \cdot \tfrac16 + 5 \cdot \tfrac16 = 1.5, $$

one term for each face. Earlier we grouped the faces by payout first, and wrote $0 \cdot \frac36 + 2 \cdot \frac26 + 5 \cdot \frac16$. The grouping works in general. Write $\{X = x\}$ for the event “$X$ takes the value $x$”, that is, the set of outcomes $\omega$ with $X(\omega) = x$. Collecting the terms of the sum that share the same value $x$ gives

$$ E[X] = \sum_{x} x\, P(X = x), $$

where the sum runs over the finitely many values that $X$ takes. The list of those values with their probabilities is called the distribution of $X$. Both formulas are worth knowing. The second is the one you compute with when you know the distribution. The first, a sum over outcomes, looks clumsier, and it is the one that makes the main theorem of this lesson easy.

There is a physical picture of the second formula. Put a weight on a number line at each value of $X$, as heavy as that value is probable. The expected value is the point where the line balances.

The payout of the carnival game as weights on a beam. Each box is one face of the die, placed at the payout for that face, so the stack heights are the probabilities 3/6, 2/6 and 1/6. The beam balances at the expected value 1.50, to the left of the 2.00 price.
The payout of the carnival game as weights on a beam. Each box is one face of the die, placed at the payout for that face, so the stack heights are the probabilities 3/6, 2/6 and 1/6. The beam balances at the expected value 1.50, to the left of the 2.00 price.
Monocurl source
let px = |v| -2.6 + v * 1.1
let BEAM = -0.75
let S = 0.66
var stack = [0, 0, 0, 0, 0, 0]
mesh boxes = block {
    for (f in range(1, 7)) {
        let v = pay(f)
        let h = len(filter(range(1, f), |g| pay(g) == v))
        let pos = [px(v), BEAM + 0.05 + S * (h + 0.5), 0]
        . Box(pos, [S - 0.06, S - 0.06], [SLATE, SLATE, AZURE, SLATE, SLATE, EMBER][v], 0.22, 2.5)
        . center{pos} Math(to_string(f), 0.8, [SLATE, SLATE, AZURE, SLATE, SLATE, EMBER][v])
    }
}
mesh beam = [
    Seg([px(0) - 0.6, BEAM, 0], [px(5) + 0.6, BEAM, 0], INK, 5),
    map(range(0, 6), |v| Seg([px(v), BEAM - 0.09, 0], [px(v), BEAM, 0], INK, 2.5)),
    map([0, 1, 3, 4, 5], |v| center{[px(v), BEAM - 0.36, 0]} Math(to_string(v), 0.78, SLATE)),
    center{[px(2), BEAM - 0.36, 0]} Math("2", 0.78, ROSE),
    center{[px(2) + 0.95, BEAM - 0.95, 0]} Cap("the price", 0.75, ROSE),
    Tip([px(2) + 0.4, BEAM - 0.85, 0], [px(2) + 0.1, BEAM - 0.55, 0], ROSE, 2.5, 0.13)
]
mesh fulcrum = [
    Shape([[px(EV), BEAM - 0.05, 0], [px(EV) - 0.2, BEAM - 0.62, 0], [px(EV) + 0.2, BEAM - 0.62, 0]], LEAF, 0.35, 3),
    center{[px(EV) - 1.5, BEAM - 1.1, 0]} Cap(str_join(["balances at ", to_string(EV, 2)]), 0.78, LEAF),
    center{[px(5) - 0.3, BEAM - 1.1, 0]} Note("payout (dollars)", 0.72)
]
mesh probs = [
    center{[px(0), BEAM + 3 * S + 0.5, 0]} Frac(3, 6, 0.85, SLATE),
    center{[px(2), BEAM + 2 * S + 0.5, 0]} Frac(2, 6, 0.85, AZURE),
    center{[px(5), BEAM + S + 0.5, 0]} Frac(1, 6, 0.85, EMBER),
    center{[1.6, 1.75, 0]} Note("one box per face of the die", 0.72)
]

Uses the shared definitions for this lesson's figures.

You can check the balance by the lever rule, in which each weight counts in proportion to its distance from the pivot. Left of 1.5 there are three boxes at distance 1.5, for a total of 4.5. Right of it there are two boxes at distance 0.5 and one at distance 3.5, also 4.5.

Notice that $1.50 is not a payout the game ever makes. An expected value does not have to be a possible value. It is a center of the distribution in the balancing sense, and nothing more is promised.

Pause and try

Let $X$ be the number shown by one fair die. Compute $E[X]$. Then let $T$ be the total of two fair dice, whose distribution over the 36 equally likely outcomes is: totals 2 through 12 occur in $1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1$ ways. Compute $E[T]$.

Show answerHide answer

$E[X] = \frac{1 + 2 + 3 + 4 + 5 + 6}{6} = \frac{21}{6} = 3.5$. For two dice, $E[T] = \frac{2 \cdot 1 + 3 \cdot 2 + 4 \cdot 3 + 5 \cdot 4 + 6 \cdot 5 + 7 \cdot 6 + 8 \cdot 5 + 9 \cdot 4 + 10 \cdot 3 + 11 \cdot 2 + 12 \cdot 1}{36} = \frac{252}{36} = 7$.

Expected values add

The two answers in that exercise are related: $7 = 3.5 + 3.5$. The expected total of two dice is the sum of the expected values of the dice. That sounds like it ought to be true, and the second computation did not use it at all. It went through the whole distribution of the total, which took eleven products.

Before trusting the shortcut we should ask what it depends on. The two dice do not influence each other. Perhaps that is why their averages add, and the rule would fail for quantities that are tied together. Here is a test with two quantities that are as tied together as they could be. On a standard die, opposite faces add to 7. Roll one die, let $X$ be the number on top and $Y$ the number on the bottom. Knowing $X$ tells you $Y$ exactly.

  1. Top face X and bottom face Y for each outcome. Each row averages 3.5.
  2. Add the two rows outcome by outcome. Every sum is 7, so the average of the sums is 7.
  3. The same 7 comes from adding the two row averages: 3.5 + 3.5.
One die, six outcomes. The top face $X$ and the bottom face $Y$ are completely dependent, since $Y = 7 - X$. Their sum is 7 for every outcome, so its average is 7, which is the average of $X$ plus the average of $Y$.
Monocurl source
let colx = |k| -2.0 + (k - 1) * 0.76
let Cell = |pos, txt, col| [Box(pos, [0.66, 0.66], col, 0.14, 2.5), center{pos} Math(txt, 0.9, col)]
let RowOf = |y, f, col| map(range(1, 7), |k| Cell([colx(k), y, 0], to_string(f(k)), col))
let mean6 = |f| sum(map(range(1, 7), |k| f(k))) / 6
let Avg = |y, f, col| center{[2.95, y, 0]} Math(to_string(mean6(f), 1), 1.1, col)
let top = |k| k
let bot = |k| 7 - k
let both = |k| top(k) + bot(k)
mesh head = [
    center{[colx(3.5), 1.8, 0]} Note("the six equally likely outcomes", 0.74),
    center{[2.95, 1.8, 0]} Note("average", 0.74)
]
mesh rows = []
mesh sums = []
mesh marks = []

slide "Top face X and bottom face Y for each outcome. Each row averages 3.5."
    rows = [
        center{[-3.1, 0.95, 0]} Cap("top", 0.8, AZURE),
        RowOf(0.95, top, AZURE),
        Avg(0.95, top, AZURE),
        center{[-3.1, 0.0, 0]} Cap("bottom", 0.8, EMBER),
        RowOf(0.0, bot, EMBER),
        Avg(0.0, bot, EMBER)
    ]
    play Fade(0.7)

slide "Add the two rows outcome by outcome. Every sum is 7, so the average of the sums is 7."
    sums = [
        Seg([-3.6, -0.6, 0], [3.45, -0.6, 0], SLATE, 2.5),
        center{[-3.1, -1.25, 0]} Cap("sum", 0.8, PLUM),
        RowOf(-1.25, both, PLUM),
        Avg(-1.25, both, PLUM)
    ]
    play Fade(0.7)

slide "The same 7 comes from adding the two row averages: 3.5 + 3.5."
    marks = [
        Outline([[2.5, -1.7, 0], [3.4, -1.7, 0], [3.4, 1.4, 0], [2.5, 1.4, 0]], LEAF, 3.5)
    ]
    play Fade(0.6)

Uses the shared definitions for this lesson's figures.

The averages added anyway: $E[X] = 3.5$, $E[Y] = 3.5$ and $E[X + Y] = 7$. The table also shows why. To average the bottom row you add six sums and divide by 6. Each of those sums is a top-row entry plus a middle-row entry, so the twelve numbers being added are the same twelve whether you add along the rows first or down the columns first. Dependence between $X$ and $Y$ changes which entries share a column. It does not change the entries, and the grand total does not care how they are paired.

This is the general proof, with $\frac16$ replaced by arbitrary probabilities.

Read the hypotheses against the table. “On the same sample space” means the two variables are defined for the same list of outcomes, so that there are columns to add down. The proof used the definition as a sum over outcomes and the fact that finite sums can be rearranged. It never mentioned an event like $\{X = x\}$, and so it never needed to know how the values of $X$ and $Y$ occur together.

Applying the theorem repeatedly extends it to any finite number of variables: $E[X_1 + X_2 + \dots + X_n] = E[X_1] + E[X_2] + \dots + E[X_n]$. The case $b = 0$ also settles the carnival game from the player’s side. Your net gain per play is $X - 2$, so its expected value is $E[X] - 2 = -0.50$ dollars.

The hat-check problem

Here is a problem where the pieces are far easier than the whole. At a party, $n$ guests check their hats. At the end of the night the attendant, who has lost the tickets, hands the hats back in a uniformly random order, so that each of the $n!$ possible arrangements is equally likely. On average, how many guests get their own hat back?

Start with the smallest interesting case and do it completely. With three guests A, B and C there are $3! = 6$ ways to return the hats.

  1. Three guests, six equally likely ways to hand back three hats. Green marks a guest who got their own hat.
  2. Count along the rows: 3, 1, 1, 0, 0, 1 guests get their own hat. That is 6 in total over 6 arrangements, an average of 1.
  3. Count down the columns instead. Each guest gets their own hat in 2 of the 6 arrangements.
  4. The same six green cells, counted by column: each guest contributes 1/3, and three guests contribute 1.
The hat-check problem for three guests. Each row is one of the six equally likely ways to hand back the hats, and a cell is green where a guest received their own. The six green cells are counted by rows and then by columns.
Monocurl source
let PEOPLE = ["A", "B", "C"]
let PERMS = [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]
let cx = |c| -2.85 + c * 1.45
let ry = |r| 1.25 - r * 0.5
let HatCell = |r, c| {
    let got = PERMS[r][c]
    var col = SLATE
    var amount = 0.1
    if (got == c) {
        col = LEAF
        amount = 0.32
    }
    return [
        Box([cx(c), ry(r), 0], [1.32, 0.42], col, amount, 2),
        center{[cx(c), ry(r), 0]} Cap(PEOPLE[got], 0.85, col)
    ]
}
let col_count = |c| count(PERMS, |p| p[c] == c)
let TOTAL = sum(map(PERMS, |p| own(p)))
mesh head = map(range(0, 3), |c| center{[cx(c), 1.8, 0]} Cap(str_join([PEOPLE[c], " gets"]), 0.76, INK))
mesh table = block {
    for (r in range(0, 6)) {
        for (c in range(0, 3)) {
            . HatCell(r, c)
        }
    }
}
mesh rowsum = []
mesh colsum = []
mesh panel = []

slide "Three guests, six equally likely ways to hand back three hats. Green marks a guest who got their own hat."
    play Fade(0.7)

slide "Count along the rows: 3, 1, 1, 0, 0, 1 guests get their own hat. That is 6 in total over 6 arrangements, an average of 1."
    rowsum = [
        center{[1.55, 1.8, 0]} Note("own hats", 0.74),
        map(range(0, 6), |r| center{[1.55, ry(r), 0]} Math(to_string(own(PERMS[r])), 0.95, LEAF))
    ]
    panel = [
        center{[2.95, 0.6, 0]} Cap(str_join(["total ", to_string(TOTAL)]), 0.82, LEAF),
        center{[2.95, -0.35, 0]} Math(str_join(["\frac{", to_string(TOTAL), "}{6} = ", to_string(TOTAL / 6, 0)]), 1.0, INK)
    ]
    play Fade(0.7)

slide "Count down the columns instead. Each guest gets their own hat in 2 of the 6 arrangements."
    panel = []
    play Fade(0.3, [&panel])
    colsum = map(range(0, 3), |c| center{[cx(c), -1.78, 0]} Cap(str_join([to_string(col_count(c)), " of 6"]), 0.78, LEAF))
    play Fade(0.6, [&colsum])

slide "The same six green cells, counted by column: each guest contributes 1/3, and three guests contribute 1."
    panel = [
        center{[2.9, 0.5, 0]} Math("\frac{2}{6} + \frac{2}{6} + \frac{2}{6}", 0.9, LEAF),
        center{[2.9, -0.45, 0]} Math("= 1", 1.05, INK)
    ]
    play Fade(0.6, [&panel])

Uses the shared definitions for this lesson's figures.

Counted by rows, the average number of guests who get their own hat is $\frac66 = 1$. The last two steps count the same six green cells by guest, and that count is the one that generalizes.

Pause and try

With four guests there are $4! = 24$ arrangements. Before computing anything, guess the average number of guests who get their own hat: more than 1, less than 1, or exactly 1? Then find it by counting green cells column by column, as in the last two steps of the figure.

Show answerHide answer

Look at one guest, say A. Guest A gets hat A in exactly those arrangements where the other three hats are distributed among the other three guests in any order, and there are $3! = 6$ of those. So A’s column has 6 green cells out of 24, and the same is true of each of the four columns. The table has $4 \times 6 = 24$ green cells in 24 rows, an average of exactly 1 per arrangement. (Counting by rows is harder: 9 arrangements have no match, 8 have one, 6 have two, none has three and 1 has four, and $\frac{8 + 12 + 4}{24} = 1$.)

Counting by rows means finding the distribution of the number of matches, which gets hard quickly as $n$ grows. Counting by columns never needs it. To write the column count in the language of this lesson, we want one random variable per guest, the one that records whether that guest’s cell is green.

An indicator turns a probability into an expected value, which lets linearity act on it. A count of how many of several events occur is the sum of their indicators, because each event that occurs adds 1 to the sum and each that does not adds 0.

The answer does not depend on $n$. With 3 guests or 3000, on average one person goes home in their own hat. As the party grows, each guest’s chance $\frac1n$ shrinks and the number of guests grows, and the two changes cancel exactly.

Why the hat-check answer does not depend on the number of guests. Each strip is one guest’s chance $1/n$ of getting their own hat, drawn as a share of a bar of length 1, with one guest’s strip in orange. As $n$ grows from 2 to 30 the strips get narrower and more numerous, and together they always fill the bar exactly.
Monocurl source
let L = -3.2
let WD = 6.4
let Y0 = -1.2
let H = 2.2
let Strips = |n| {
    let k = to_int(floor(n + 0.001))
    var parts = []
    for (i in range(1, k)) {
        var amount = 0.2
        if (mod(i, 2) == 1) {
            amount = 0.42
        }
        parts .= Box([L + (i + 0.5) * WD / k, Y0 + H / 2, 0], [WD / k, H], AZURE, amount, 1.5)
    }
    parts .= Box([L + 0.5 * WD / k, Y0 + H / 2, 0], [WD / k, H], EMBER, 0.45, 2.5)
    parts .= center{[-1.55, 1.75, 0]} scale{1.25} color{INK} Number(k, 0)
    parts .= center{[1.75, 1.75, 0]} scale{1.25} color{EMBER} Number(1 / k, 3)
    return parts
}
mesh scaffold = [
    Seg([L, Y0 - 0.2, 0], [L + WD, Y0 - 0.2, 0], SLATE, 2.5),
    map([0, 0.25, 0.5, 0.75, 1], |v| [
        Seg([L + v * WD, Y0 - 0.28, 0], [L + v * WD, Y0 - 0.12, 0], SLATE, 2),
        center{[L + v * WD, Y0 - 0.5, 0]} Math(to_string(v, 2), 0.72, SLATE)
    ]),
    center{[0, Y0 - 0.85, 0]} Note("chances added guest by guest", 0.72),
    center{[-2.5, 1.75, 0]} Cap("guests:", 0.8, INK),
    center{[0.2, 1.75, 0]} Cap("chance for each:", 0.8, EMBER)
]
mesh strips = Strips(n: 2)

slide "grow"
    play Wait(1.2)
    strips.n = 30
    play Lerp(7, [&strips], |u| u * u)
    play Wait(1.6)

Uses the shared definitions for this lesson's figures.

I find this result hard to believe on first meeting, so here is a check that involves no theory. The figure shuffles ten hats 2000 times and records how many guests got their own hat each time.

A simulation of 2000 random returns of 10 hats. The bars show how often 0, 1, 2, ... guests got their own hat, and the green marks are the exact probabilities. No match and one match are the most common results, about 37% each. The average number of matches ends at 0.96 in this run: an average over 2000 shuffles typically misses the expected value 1 by about 0.02, so a miss of 0.04 is on the large side of ordinary.
Monocurl source
let NH = 10
let TRIALS = 2000
let STEP = 20
var hist = [0, 0, 0, 0, 0, 0, 0]
var total = 0
var snaps = [[hist, 0]]
for (t in range(1, TRIALS + 1)) {
    let m = own(shuffle(NH, t, 6))
    let b = to_int(min(m, 6))
    hist[b] = hist[b] + 1
    total = total + m
    if (mod(t, STEP) == 0) {
        snaps .= [hist, total]
    }
}
let SNAPS = snaps
# exact distribution: C(n, k) D(n - k) / n!, where D counts arrangements with no match
var der = [1, 0]
for (m in range(2, NH + 1)) {
    der .= (m - 1) * (der[m - 1] + der[m - 2])
}
let DER = der
let exact = |k| choose(NH, k) * DER[NH - k] / factorial(NH)
let fh = Frame(-0.6, 6.6, 0, 0.5, [-0.95, 0.1, 0], 4.8, 2.9)
let Hist = |j| {
    let s = to_int(floor(j + 0.001))
    let n = s * STEP
    var parts = []
    for (k in range(0, 7)) {
        let f = SNAPS[s][0][k] / n
        if (f > 0.004) {
            parts .= Box(at(fh, k, f / 2), [0.48, f * 2.9 / 0.5], AZURE, 0.3, 2.5)
        }
    }
    parts .= center{[3.0, 0.65, 0]} scale{1.3} color{INK} Number(n, 0)
    parts .= center{[3.0, -0.85, 0]} scale{1.3} color{EMBER} Number(SNAPS[s][1] / n, 2)
    return parts
}
mesh frame = [
    GridLines(fh, [], [0.1, 0.2, 0.3, 0.4, 0.5]),
    Seg(at(fh, -0.6, 0), at(fh, 6.6, 0), SLATE, 2.5),
    Seg(at(fh, -0.6, 0), at(fh, -0.6, 0.5), SLATE, 2.5),
    map([0.1, 0.2, 0.3, 0.4, 0.5], |v| center{[fx(fh, -0.6) - 0.36, fy(fh, v), 0]} Math(to_string(v, 1), 0.72, SLATE)),
    map(range(0, 6), |k| center{[fx(fh, k), fy(fh, 0) - 0.3, 0]} Math(to_string(k), 0.75, SLATE)),
    center{[fx(fh, 6), fy(fh, 0) - 0.3, 0]} Math("6+", 0.75, SLATE),
    center{[fx(fh, 3), fy(fh, 0) - 0.68, 0]} Note("guests who got their own hat", 0.72),
    center{[fx(fh, -0.6) + 1.15, fy(fh, 0.5) + 0.35, 0]} Note("fraction of shuffles", 0.72),
    center{[3.0, 1.7, 0]} Cap("simulation", 0.75, SLATE),
    center{[3.0, 1.1, 0]} Cap("shuffles", 0.78, INK),
    center{[3.0, -0.4, 0]} Cap("average matches", 0.78, EMBER)
]
mesh bars = Hist(j: 1)
mesh exact_marks = [
    map(range(0, 6), |k| Seg(at(fh, k - 0.36, exact(k)), at(fh, k + 0.36, exact(k)), LEAF, 4)),
    Seg([0.2, 1.2, 0], [0.6, 1.2, 0], LEAF, 4),
    center{[0.4, 0.9, 0]} Cap("exact", 0.72, LEAF)
]

slide "run"
    play Wait(0.6)
    bars.j = TRIALS / STEP
    play Lerp(7, [&bars], linear)
    play Wait(1.8)

Uses the shared definitions for this lesson's figures.

The average ends at 0.96 in this run. The theorem is about the expected value, and a finite run only lands near it; the runs in the next figure land a little above 1. The histogram also shows something the theorem does not say. An average of 1 does not mean that one match is what usually happens: no match at all is just as common, and two or three matches are not rare.

The last figure repeats the experiment for parties of four different sizes.

The average number of guests who got their own hat in 1000 simulated random returns, for parties of 3, 10, 30 and 100 guests. All four averages are close to the dashed line at 1.
The average number of guests who got their own hat in 1000 simulated random returns, for parties of 3, 10, 30 and 100 guests. All four averages are close to the dashed line at 1.
Monocurl source
let SIZES = [3, 10, 30, 100]
let RUNS = 1000
let avg_for = |n, salt| {
    var total = 0
    for (t in range(1, RUNS + 1)) {
        total = total + own(shuffle(n, t, salt))
    }
    return total / RUNS
}
let AVGS = [avg_for(3, 11), avg_for(10, 12), avg_for(30, 13), avg_for(100, 14)]
let fb = Frame(0, 4, 0, 1.4, [0.15, 0.05, 0], 6.2, 3.0)
mesh frame = [
    GridLines(fb, [], [0.5, 1]),
    Seg(at(fb, 0, 0), at(fb, 4, 0), SLATE, 2.5),
    Seg(at(fb, 0, 0), at(fb, 0, 1.4), SLATE, 2.5),
    map([0.5, 1], |v| center{[fx(fb, 0) - 0.4, fy(fb, v), 0]} Math(to_string(v, 1), 0.75, SLATE)),
    center{[fx(fb, 2), fy(fb, 1.4) + 0.3, 0]} Note("simulation: average matches over 1000 shuffles", 0.74)
]
mesh bars = map(range(0, 4), |i| [
    Box(at(fb, i + 0.5, AVGS[i] / 2), [0.95, AVGS[i] * 3.0 / 1.4], AZURE, 0.3, 3),
    center{at(fb, i + 0.5, AVGS[i]) + [0, 0.28, 0]} Math(to_string(AVGS[i], 3), 0.82, AZURE),
    center{[fx(fb, i + 0.5), fy(fb, 0) - 0.35, 0]} Cap(str_join([to_string(SIZES[i]), " guests"]), 0.78, INK)
])
mesh target = stroke{LEAF, 3} dashed{[0.14, 0.09]} Line(at(fb, 0, 1), at(fb, 4, 1))

Uses the shared definitions for this lesson's figures.

Where independence was not used

The proof never asked whether the indicators $X_1, \dots, X_n$ are independent, and they are not. Take $n = 2$. Either both guests get their own hats or they swap, so $X_1$ and $X_2$ are always equal. In the language of the lesson on conditional probability, where events $A$ and $B$ are called independent when $P(A \cap B) = P(A)P(B)$, we have $P(A_1 \cap A_2) = \frac12$ while $P(A_1)P(A_2) = \frac14$. For larger $n$ the dependence is weaker and still present: if the first $n - 1$ guests all have their own hats, the last one must too. Linearity ignores all of it.

The same freedom does not extend to products. For the top face $X$ of one die,

$$ E[X \cdot X] = \frac{1 + 4 + 9 + 16 + 25 + 36}{6} = \frac{91}{6} \approx 15.17, \qquad E[X] \cdot E[X] = 3.5^2 = 12.25. $$

So $E[XY] = E[X]\,E[Y]$ can fail, here with $Y = X$. That product rule is a true theorem when $X$ and $Y$ are independent, and its proof uses the independence.

One more boundary. Our definition was for a finite sample space. When an experiment has infinitely many outcomes the expected value is an infinite series, and it may fail to converge. Flip a fair coin until the first head, and suppose the game pays $2^k$ dollars if that head arrives on flip $k$, which has probability $2^{-k}$. The series for the expected payout is $2 \cdot \frac12 + 4 \cdot \frac14 + 8 \cdot \frac18 + \dots = 1 + 1 + 1 + \dots$, which has no finite sum. This is the St. Petersburg game, and it has no expected value in our sense.

Worked examples

Common mistakes

Stepping back

The carnival question was settled by a weighted average, and that part of the lesson is bookkeeping. The harder idea is the choice between two ways of adding up the same table. Summing over the values of a complicated quantity requires its distribution, which can be out of reach. Summing over outcomes lets a total be split into pieces before any averaging is done, and then each piece can be averaged alone. For the hats, the pieces were single guests, each with an easy probability of $\frac1n$.

The method to keep is this: when asked for the expected number of something, write the count as a sum of indicators and add their probabilities. The next lesson applies it to the number of successes in $n$ repeated trials, where it gives the mean of the binomial distribution in one line.

Check yourself

A raffle sells 500 tickets at $2 each. One ticket, drawn at random, wins $400, and the others win nothing. What is the expected net gain from buying one ticket?

Problems

The first two repeat the lesson’s computations. The middle four ask you to choose the indicators yourself. The last two extend the lesson.

Problem 1

A game costs $1.50. You draw one card from a well-shuffled standard deck of 52. An ace pays $10, a jack, queen or king pays $2, and any other card pays nothing. Find the expected payout and the expected net gain.

Show solutionHide solution

There are 4 aces and 12 jacks, queens and kings. The payout $X$ takes the value 10 with probability $\frac{4}{52}$, the value 2 with probability $\frac{12}{52}$, and 0 otherwise, so

$$ E[X] = 10 \cdot \frac{4}{52} + 2 \cdot \frac{12}{52} = \frac{64}{52} = \frac{16}{13} \approx 1.23 \text{ dollars}. $$

The net gain is $X - 1.5$, and by linearity $E[X - 1.5] = \frac{16}{13} - \frac{3}{2} = -\frac{7}{26} \approx -0.27$ dollars. The player loses about 27 cents per game on average.

Problem 2

Ten fair dice are rolled. Find (a) the expected total and (b) the expected number of sixes.

Show solutionHide solution

(a) The total is the sum of the ten faces, each with expected value 3.5, so by linearity the expected total is $10 \times 3.5 = 35$. (b) For each die let its indicator be 1 if it shows a six; each indicator has expected value $\frac16$. The number of sixes is the sum of the ten indicators, so its expected value is $\frac{10}{6} = \frac53 \approx 1.67$.

Problem 3

Two fair dice are rolled. Let $M$ be the larger of the two numbers and $m$ the smaller (if the dice agree, both equal the common value). (a) Find $E[M]$ from its distribution. (b) Find $E[m]$ without computing another distribution.

Show solutionHide solution

(a) $M \le k$ exactly when both dice are at most $k$, which covers $k^2$ of the 36 cells. So $M = k$ on $k^2 - (k-1)^2 = 2k - 1$ cells: 1, 3, 5, 7, 9, 11 cells for $k = 1, \dots, 6$. Then

$$ E[M] = \frac{1 \cdot 1 + 2 \cdot 3 + 3 \cdot 5 + 4 \cdot 7 + 5 \cdot 9 + 6 \cdot 11}{36} = \frac{161}{36} \approx 4.47. $$

(b) For every outcome, $M + m$ is the total of the two dice, whose expected value is 7. By linearity $E[M] + E[m] = 7$, so $E[m] = 7 - \frac{161}{36} = \frac{91}{36} \approx 2.53$. The variables $M$ and $m$ are strongly dependent, and that did not matter.

Problem 4

A standard deck of 52 cards is shuffled. You turn the cards over one at a time while calling out “ace, two, three, ..., queen, king” and then starting over, four times through. On average, how many times does the card you turn over have the rank you call?

Show solutionHide solution

For each position $i = 1, \dots, 52$ let $X_i$ be the indicator that the card in position $i$ has the rank called at position $i$. In a uniformly random shuffle the card in any fixed position is equally likely to be any of the 52 cards, and 4 of them have the required rank, so $E[X_i] = \frac{4}{52} = \frac{1}{13}$. The number of matches is $X_1 + \dots + X_{52}$, with expected value $52 \cdot \frac{1}{13} = 4$. The positions are dependent (a deck has only four aces), which linearity does not notice.

Problem 5

A fair coin is flipped 10 times. A “double head” is a pair of consecutive flips that are both heads, and overlapping pairs count separately, so HHH contains two. Find the expected number of double heads.

Show solutionHide solution

There are 9 pairs of consecutive positions: $(1,2), (2,3), \dots, (9,10)$. For each, the indicator of “both are heads” has expected value $\frac12 \cdot \frac12 = \frac14$. The number of double heads is the sum of these 9 indicators, so its expected value is $\frac94 = 2.25$. Neighboring indicators share a flip and are dependent, which again makes no difference to the sum of expectations.

Problem 6

A student claims: “A fair die shows $X$. Since $E[X] = 3.5$, the expected value of $1/X$ is $1/3.5 \approx 0.286$.” Compute $E[1/X]$ and say what went wrong.

Show solutionHide solution

From the definition,

$$ E\!\left[\frac1X\right] = \frac16\left(1 + \frac12 + \frac13 + \frac14 + \frac15 + \frac16\right) = \frac16 \cdot \frac{147}{60} = \frac{49}{120} \approx 0.408. $$

The student applied the reciprocal to the expected value instead of to the values. Linearity allows $E$ to pass through sums and constant multiples only, and $x \mapsto 1/x$ is neither. Here the small values of $X$ produce large reciprocals (the outcome 1 alone contributes $\frac16$), which pulls the average of $1/X$ well above $1/3.5$.

Problem 7

In the hat-check problem with $n \ge 2$ guests, let $X_i$ be the indicator that guest $i$ gets their own hat and $X = X_1 + \dots + X_n$. (a) For $i \ne j$, show that $E[X_i X_j] = \frac{1}{n(n-1)}$, and compare with $E[X_i]\,E[X_j]$. (b) Use (a) and linearity to show that $E[X^2] = 2$ for every $n \ge 2$. (c) Check (b) against the table for $n = 3$.

Show solutionHide solution

(a) The product $X_i X_j$ equals 1 exactly when both guests get their own hats, so it is the indicator of $A_i \cap A_j$. That event fixes two hats and leaves the other $n - 2$ free, so it contains $(n-2)!$ arrangements and

$$ E[X_i X_j] = \frac{(n-2)!}{n!} = \frac{1}{n(n-1)}. $$

This is larger than $E[X_i]E[X_j] = \frac{1}{n^2}$: one guest getting the right hat makes it slightly more likely that another does, since one wrong option has been removed.

(b) Expand the square: $X^2 = \sum_i X_i^2 + \sum_{i \ne j} X_i X_j$, where the second sum runs over the $n(n-1)$ ordered pairs with $i \ne j$. Since an indicator is 0 or 1, $X_i^2 = X_i$. By linearity,

$$ E[X^2] = \sum_i E[X_i] + \sum_{i \ne j} E[X_i X_j] = n \cdot \frac1n + n(n-1) \cdot \frac{1}{n(n-1)} = 1 + 1 = 2. $$

(c) For $n = 3$ the six arrangements give $X = 3, 1, 1, 0, 0, 1$, so $E[X^2] = \frac{9 + 1 + 1 + 0 + 0 + 1}{6} = \frac{12}{6} = 2$.

Problem 8

Let $X$ be a random variable on a finite sample space and write $\mu = E[X]$. (a) Show that $E[X - \mu] = 0$. (b) For a real number $c$, show that $E[(X - c)^2] = E[(X - \mu)^2] + (\mu - c)^2$. (c) Conclude that $c = \mu$ is the unique number that minimizes the average squared distance $E[(X - c)^2]$.

Show solutionHide solution

(a) By linearity with the constant $-\mu$: $E[X - \mu] = E[X] - \mu = 0$. This is the balance statement: deviations above and below the mean cancel on average.

(b) Write $X - c = (X - \mu) + (\mu - c)$, where $\mu - c$ is a constant. Squaring,

$$ (X - c)^2 = (X - \mu)^2 + 2(\mu - c)(X - \mu) + (\mu - c)^2. $$

Take expectations and use linearity, with $2(\mu - c)$ and $(\mu - c)^2$ as constants:

$$ E[(X - c)^2] = E[(X - \mu)^2] + 2(\mu - c)\,E[X - \mu] + (\mu - c)^2. $$

The middle term is zero by part (a).

(c) The first term on the right does not involve $c$, and the second is $(\mu - c)^2 \ge 0$ with equality only when $c = \mu$. So $E[(X - c)^2]$ is smallest exactly at $c = \mu$. The minimum value, $E[(X - \mu)^2]$, measures how spread out $X$ is around its mean. It is called the variance, and the next lesson uses it.

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 rnd = |i, salt| mod(seeded_random(i, salt) + 1, 1)
let roll = |i, salt| to_int(floor(6 * rnd(i, salt))) + 1

# the carnival game: pay PRICE, roll one die, collect pay(face)
let PRICE = 2
let pay = |face| {
    if (face == 6) {
        return 5
    }
    if (face >= 4) {
        return 2
    }
    return 0
}
let EV = sum(map(range(1, 7), |f| pay(f))) / 6
let pay_col = |v| {
    if (v == 5) {
        return EMBER
    }
    if (v == 2) {
        return AZURE
    }
    return tint(SLATE, 0.45)
}

# a random arrangement of 0..n-1 (Fisher-Yates), reproducible from the trial number t
let shuffle = |n, t, salt| {
    var p = map(range(0, n), |i| i)
    var i = n - 1
    while (i > 0) {
        let j = to_int(floor((i + 1) * rnd(t * 1009 + i, salt)))
        let tmp = p[i]
        p[i] = p[j]
        p[j] = tmp
        i = i - 1
    }
    return p
}
# how many people end up with their own hat
let own = |p| {
    var c = 0
    for (i in range(0, len(p))) {
        if (p[i] == i) {
            c = c + 1
        }
    }
    return c
}
let Frac = |num, den, size = 1.0, col = INK| Math(str_join(["\frac{", to_string(num), "}{", to_string(den), "}"]), size, col)