Books · The Fiddler: Solutions
Chapter 1
Can You Fix the Random Number Generator?
A calculator’s random number key should return a value spread evenly between and , with every value equally likely. This one is broken: it returns a value spread evenly between and some fixed , where is an unknown number between and . Before you switch it on you have no idea what is, so you treat it as equally likely to be anywhere in that range. You press the key once and read off exactly . Given that single reading, what is the expected value of ?
For extra credit: a second broken calculator works the same way, with its own unknown spread evenly over to . This time you do not see the reading itself, only that it landed somewhere between and . Now what is the expected value of ?
The Fiddler, Zach Wissner-Gross, June 19, 2026(original post)
Solution
Everything turns on the ceiling , the value the broken calculator never climbs above. Before the key is pressed could sit anywhere in with equal claim, a flat prior , and the single reading is the only thing that sharpens that belief. The expected value we are after is the average of the sharpened belief, so the task is to see how the reading reshapes it.
Start from what a calculator of a known ceiling does. One with ceiling scatters its output evenly across , so it returns a value near with density , as long as does not exceed , for a machine can never report a number above its own ceiling. Now read this the other way, as a statement about the ceiling for the value we actually saw, and two things stand out. A reading of throws out every ceiling below outright, since those machines could not have produced it; and among the ceilings that survive, the lower ones are the better bet, because a calculator with a narrow range packs its output into a short interval and is the likelier source of any particular reading. That second effect is precisely the factor .
Those two observations are Bayes’ rule for this problem. The sharpened belief in , its posterior density, is the prior cut down to the survivors and tilted by , and dividing by its total turns the proportionality into a genuine density. The average ceiling is this density weighted by and summed across the survivors, where the weight neatly cancels the and leaves a flat integrand, For the reading , where , this comes to The reading holds the ceiling above , and the pull toward smaller ceilings then drags the average down from the midpoint of the surviving range, settling at (Figure, left).
Another way. The tilt carries a tidy reading of its own. Setting turns the weight into plain , so once the reading is in hand is spread evenly between and . The ceiling is therefore for uniform on , sliding from the floor up to as runs from to , and averaging over returns the same value, which is the mean of a log-uniform value: the plain gap over the logarithmic gap .
Extra credit
This time the reading itself is hidden, and all we are told is that it landed below . The same machinery carries over, except that the evidence is now a whole event rather than one value, so each ceiling is judged by how often it lands there. A calculator with ceiling never climbs past , so it falls below for certain; one with manages it only part of the time, with probability , the share of its range that lies under . Writing and weighting the flat prior by this chance gives the posterior Its total is the overall chance of a low reading, and the same integral carrying an extra factor of gives the unnormalised mean, Their ratio is the answer, and at it works out to The two halves of the puzzle send the same number in opposite directions. Seeing it exactly proves the ceiling reaches that high and lifts the estimate to , whereas learning only that the reading stayed below it points to a low ceiling, since those are the machines bound to land there, and the estimate falls to (Figure, right).
Another way. The extra credit also drops out of the main answer, by averaging it over the reading we never saw. A reading near turns up with density across , so the law of total expectation gives The frequency is the very denominator buried inside , so the two cancel and leave the same value once more.
The computation
The check simulates the broken calculator rather than the formula: draw a bound uniformly on , draw a reading uniformly on , and keep the runs that match what was observed; the average bound over those runs is the answer. An exact reading of has probability zero, so we keep readings inside a thin window and watch the estimate settle as the window shrinks; the event “below ” has positive probability, so plain accept-and-average is exact.
import random
from math import log
random.seed(0)
# Main: the reading came out exactly 0.5. Condition on a thin window |x-0.5|<eps;
# as the window shrinks the estimate approaches E[a | X = 0.5].
def estimate_exact(eps, N=20_000_000):
total = count = 0
for _ in range(N):
a = random.random() # prior: a ~ Uniform(0, 1)
x = random.random() * a # broken: x ~ Uniform(0, a)
if abs(x - 0.5) < eps:
total += a; count += 1
return total / count
for eps in (0.02, 0.01, 0.004):
print(f"exact reading, window {eps:<5}: E[a] ~ {estimate_exact(eps):.4f}")
print(f"closed form 1/(2 ln 2) = {1 / (2 * log(2)):.4f}")
# Extra credit: the reading is only known to be below 0.5. A finite-probability
# event, so a plain accept/reject Monte Carlo is exact.
def estimate_below(N=40_000_000):
total = count = 0
for _ in range(N):
a = random.random()
x = random.random() * a
if x < 0.5:
total += a; count += 1
return total / count
print(f"reading below 0.5: E[a] ~ {estimate_below():.4f}")
print(f"closed form (3/4)/(1+ln 2) = {0.75 / (1 + log(2)):.4f}")
# exact reading, window 0.02 : E[a] ~ 0.7210
# exact reading, window 0.01 : E[a] ~ 0.7211
# exact reading, window 0.004: E[a] ~ 0.7209
# closed form 1/(2 ln 2) = 0.7213
# reading below 0.5: E[a] ~ 0.4429
# closed form (3/4)/(1+ln 2) = 0.4430
The two alternate derivations are identities, not experiments, so a symbolic check suits them: the log-uniform mean for the main puzzle, and the total-expectation quotient for the extra credit, with the marginal computed rather than assumed.
import sympy as sp
x, v, a, c = sp.symbols("x v a c", positive=True)
# Main, log-uniform route: given the reading x, ln(a) is uniform, so a = x**(1-V),
# V uniform on [0, 1]. The x < 1 branch of its mean is (x-1)/ln(x):
main = sp.integrate(x ** (1 - v), (v, 0, 1))
print("main E[a] = (x-1)/ln x ; at x=1/2 ->",
float(((x - 1) / sp.log(x)).subs(x, sp.Rational(1, 2))))
# Extra credit, law of total expectation over the hidden reading.
fX = sp.integrate(1 / a, (a, x, 1)) # marginal of the reading = -ln x
Emean = (x - 1) / sp.log(x) # = E[a | X = x]
tower = sp.simplify(sp.integrate(Emean * fX, (x, 0, c)) / sp.integrate(fX, (x, 0, c)))
print("EC E[a|X<c] =", tower, "; at c=1/2 ->",
float(tower.subs(c, sp.Rational(1, 2))))
# main E[a] = (x-1)/ln x ; at x=1/2 -> 0.7213475204444817
# EC E[a|X<c] = (c - 2)/(2*(log(c) - 1)) ; at c=1/2 -> 0.44296208186223096
E[a] = (1 − x) / (−ln x) = 0.721