Books · The Fiddler: Solutions
Chapter 40
How Far Can You Run Before Sundown?
A trail run ends at sundown, p.m. There are four loops, of , , , and miles, and after finishing each loop you are randomly (uniformly) assigned the next one. Only completed loops count. It is p.m., you have just finished a loop, and you run a steady -minute mile. On average, how many miles will you complete in the minutes before sundown?
The Fiddler, Zach Wissner-Gross, August 22, 2025(original post)
Solution
The four loops take , , , and minutes. With minutes left you are handed a uniformly random loop; if it fits you run it and continue, otherwise the day ends. So the expected remaining score satisfies over the four loops. Evaluating from gives the exact
The computation
Encode the assignment process: from minutes left, average over the four equally likely loops, running each (and recursing) only if it fits before sundown. Exact fractions, memoised on the time remaining.
from fractions import Fraction as F
from functools import lru_cache
loops = [(F(1), 10), (F(3), 30), (F(7, 2), 35), (F(9, 2), 45)] # (miles, minutes)
@lru_cache(None)
def E(T):
return sum((mi + E(T - t)) if t <= T else F(0) for mi, t in loops) / 4
print(E(65), round(float(E(65)), 5)) # 19933/4096 4.86646
Extra Credit
Once during the race, if you dislike the loop you have just been assigned, you may take a mulligan: discard it and draw a fresh assignment (you must then keep the redraw). Using the mulligan optimally, what score can you expect?
Solution
Add a state for whether the mulligan is still available; for each draw you take the better of running it now or rerolling with the mulligan spent. This yields (The source value is paywalled; this exact figure is my own.)
The computation
The same recursion with a mulligan flag: on each assignment, compare running it now against rerolling once (mulligan spent) and take the larger.
@lru_cache(None)
def Em(T, m): # m = mulligans left
tot = F(0)
for mi, t in loops:
run = (mi + Em(T - t, m)) if t <= T else F(0)
tot += max(run, Em(T, m - 1)) if m > 0 else run # reroll option
return tot / 4
print(Em(65, 1), round(float(Em(65, 1)), 5)) # 21921/4096 5.35181