Chapter 205
How Far Would You Go To Rig A Coin Flip?
Riddler Express
Anna and Barry have a fair coin marked heads and tails, and want a game whose outcome gives Anna a target probability of winning. How can they devise games that give Anna a in chance, a in chance, and a in chance?
The Riddler, FiveThirtyEight, November 2, 2018(original post)
Solution
A fair coin gives only dyadic probabilities directly. Non-dyadic targets like and have to come from repeating the coin until some event happens: a geometric series can sum to any rational in .
Anna wins in : alternate single flips, Barry first. Barry flips, then Anna flips, then Barry, and so on, and the first head wins. On any given flip the flipper wins with probability . Let be Anna’s win probability. Then Barry wins on his very first flip with probability , and otherwise (also probability ) Anna becomes the next flipper, which by symmetry of the rule is the same game with roles swapped. So
Anna wins in : Barry flips twice, any head and he wins. Barry takes two flips; he wins on any head, loses only on two consecutive tails. Two tails have probability , which is Anna’s win probability:
Anna wins in : alternate two-flip turns, Barry first. Barry flips twice, then Anna flips twice, then Barry again, and so on. The first head ends the game. Whoever is taking their turn wins their two flips with probability and passes the game on with probability . So Barry wins with probability so Anna wins with probability .
The pattern generalises. To give Anna a in chance, alternate -flip turns starting with Barry, and let the first head decide; then , so Anna’s chance is . The case recovers , recovers , and the same shape would give .
The computation
Re-encode each game and run it times. The empirical win rates must match , , to Monte Carlo precision.
For the game, repeatedly flip alternating Barry/Anna; first head wins.
For the game, Barry flips two; any head wins for Barry.
For the game, repeatedly flip two-Barry / two-Anna turns; first head wins.
Compare each empirical Anna-win rate to the target.
import random
def game_third(): # Anna wins 1/3
turn = "B"
while True:
if random.random() < 0.5:
return "A" if turn == "A" else "B"
turn = "A" if turn == "B" else "B"
def game_quarter(): # Anna wins 1/4
for _ in range(2):
if random.random() < 0.5:
return "B"
return "A"
def game_fifth(): # Anna wins 1/5
turn = "B"
while True:
for _ in range(2):
if random.random() < 0.5:
return turn
turn = "A" if turn == "B" else "B"
random.seed(0)
TRIALS = 1_000_000
for name, g, target in [("1/3", game_third, 1/3),
("1/4", game_quarter, 1/4),
("1/5", game_fifth, 1/5)]:
wins = sum(1 for _ in range(TRIALS) if g() == "A")
print(f"{name}: empirical {wins/TRIALS:.4f}, target {target:.4f}")
The script prints empirical Anna-win rates , , , matching the boxed answer.
Riddler Classic
All seats in Riddler Nation’s Senate are decided by a virtual coin flip per seat, Programmers on heads and Theorists on tails. The Programmers control the coin; the Theorists collect every result and can challenge in the Statistical Court (an honest, learned panel of statisticians) at any time, with no statute of limitations. (a) If the coin is fair, what is the probability a given party wins a simple majority? a -seat supermajority? (b) What annual weighting gives the Programmers a chance of a supermajority in a single year? How many such years before the Theorists can prove (with at least confidence) that the coin is biased? (c) If the coin is weighted permanently for elections, how heavy a weighting escapes a challenge over the whole century? How many supermajorities does that buy? (d) What is the optimal cheating strategy for perpetual escape? (e) Does varying the weight on a seat-by-seat basis within an election help?
The Riddler, FiveThirtyEight, November 2, 2018(original post)
Solution
Each Senate seat is one Bernoulli trial; an election is a sum of independent trials. With counting Programmer seats, is a simple majority and a supermajority.
(a) Fair coin baselines. For , So a given party wins a simple majority about of the time and a supermajority about of the time (rounded to ).
(b) One-year boost to a coin-flip on the supermajority. We need the with . Binomial CDF inversion gives The Theorists test the fairness null on the cumulative flip data. After years of cheating at and fair years (say the Programmers cheat once at the start), the total head count has mean on the bias and a roughly Gaussian sampling distribution under the null with standard deviation . The fair distribution exceeds the observed total with probability at least when the excess over is at most . Each cheated year contributes an expected excess of , so the Programmers escape detection so long as , that is, year (at the expected excess is already , well within the tail under the null with negligible probability of escape). They get away with this aggressive weighting only once.
(c) Permanent weighting for elections. The Theorists’ challenge looks at the cumulative count over flips. Under fair flipping, the th percentile of is so a permanent weight keeps the cheating Programmers’ expected total inside the fair th-percentile envelope. At , the expected number of supermajorities over elections is
(d) Optimal strategy. The optimal strategy lets the Programmers oscillate. Early years they weight heavily and harvest more supermajorities, then dial the weight back so that the running cumulative head count stays below the fair-coin percentile in every year (the court can examine any sub-window). In the limit, the strategy approaches a fair coin asymptotically, but the early years pay out extra supermajorities, beating the constant in expected total. The exact optimum is the solution of a constrained scheduling problem maximising subject to the running-window constraint, which is solvable numerically and gives the heads-high-early, fair-later schedule.
(e) Per-seat weighting within one election. The total Programmer count is the sum of independent Bernoullis with weights . The Theorists’ running-total test cares only about the mean , not the spread, so any constraint they impose binds the average. For a fixed average , the variance of the seat count is , which by Jensen’s inequality is maximised when all (uniform). Heterogeneous per-seat weights only reduce the variance, which (when ) shrinks the upper tail and so reduces the supermajority chance. Net: per-seat heterogeneity does not help, and a Programmer who has decided on an average weight is best off applying it uniformly. The seat-by-seat lever is illusory.
The computation
Compute the binomial baselines exactly, invert the equation for , compute of , and evaluate the expected supermajority count under the permanent-weight ceiling.
Closed-form binomial tails via
math.comb(no sampling needed).Bisection on in to find .
Compute of exactly.
Plug back in for expected supermajorities.
from math import comb
def tail(n, p, k): # P(X >= k) for Binomial(n,p)
q = 1 - p
return sum(comb(n, j) * p**j * q**(n - j) for j in range(k, n + 1))
print(f"Fair: P(>=51 of 100) = {tail(100, 0.5, 51):.4f}")
print(f"Fair: P(>=60 of 100) = {tail(100, 0.5, 60):.6f}")
# Bisection for the one-year cheat weight
lo, hi = 0.5, 0.7
for _ in range(80):
mid = (lo + hi) / 2
lo, hi = (mid, hi) if tail(100, mid, 60) < 0.5 else (lo, mid)
print(f"One-year cheat weight (50% of supermajority): {hi:.4f}")
# 99th percentile of Binomial(10000, 0.5): smallest q with P(X<=q) >= 0.99.
# Use Decimal to avoid float underflow on comb(10000,k) * 2**(-10000).
from decimal import Decimal, getcontext
getcontext().prec = 60
n = 10_000
half_n = Decimal(2) ** n
cum = Decimal(0)
target = Decimal("0.99")
for k in range(n + 1):
cum += Decimal(comb(n, k)) / half_n
if cum >= target:
q = k
print(f"99th percentile of Binomial(10000, 0.5) = {q}")
break
p_perm = q / n
print(f"Permanent safe weight = {p_perm:.4f}")
print(f"Expected supermajorities in 100 years at p* = "
f"{100 * tail(100, p_perm, 60):.2f}")
The script prints , , the one-year boost , , and an expected supermajorities at the permanent ceiling, matching every numeric headline.