Chapter 181
How Fast Can You Type A Million Letters?
Riddler Express
people need to use a row of urinals. They obey a fixed rule: the first person picks one of the two ends; each subsequent person picks the unoccupied urinal that maximises the distance to the nearest already-occupied one. Anyone who would be forced into a urinal adjacent to an occupied one simply leaves. What is the minimum that accommodates all people at the same time?
The Riddler, FiveThirtyEight, April 27, 2018(original post)
Solution
Why the naive answer is wrong. If the goal were to seat people non-adjacently, urinals always suffice (the alternating pattern XOXOXX). But the rule of play is not "any non-adjacent placement"; it is the strict greedy distance-maximiser. That rule can leave gaps that nobody is willing to use, so the minimum that the greedy process actually fills is generally larger than .
What the greedy rule does on a row of urinals. The first two people take the two ends. After that, every choice splits the longest current empty gap by adding a person in its middle. A gap of length (the number of empty urinals strictly between two occupied positions) becomes two sub-gaps of lengths and when a person sits at the most central allowed slot. A gap of length or is dead (no person will sit there because that would be adjacent to an existing user).
So the question reduces to: starting with one gap of length between the two end-takers, how many extra people can we seat by repeatedly splitting the largest live gap (length ) into two halves?
Powers-of-two arithmetic. Splitting a gap of length uses one person and produces two sub-gaps of lengths whose sizes are determined by . The cleanest case is when for some : the splitting is binary, every internal vertex of a complete binary tree on leaves seats one person, and the total number of seated people is exactly end-takers plus the binary-tree count , giving for . So when is a power of two, the minimum is exactly .
For other , the central gap has to be at least : anything smaller cannot be split into non-dead pieces by the greedy rule. And taking is enough (the splitter never gets stuck in this regime). Therefore with .
Reading the formula. For the formula gives , matching the official’s worked example ( does not fit in ; it needs ). For at the formula matches the tight ; for just above such a power of two the bound jumps to , exactly the loose case where the greedy is most wasteful.
The computation
Encode the greedy rule directly, sweep upward, and read off the minimum that seats all people.
Start with the two ends occupied.
Repeatedly choose the unoccupied position whose distance to the nearest occupied position is largest, ties broken arbitrarily, and place a person there.
Stop when the best achievable distance drops to ; that count is the number of people the greedy process seats.
import math
def seated(M):
if M <= 0: return 0
if M == 1: return 1
occ = [False] * M
occ[0] = occ[-1] = True
count = 2
while True:
best_d, best_i = -1, -1
for i in range(M):
if occ[i]: continue
d = min(abs(i - j) for j in range(M) if occ[j])
if d > best_d:
best_d, best_i = d, i
if best_d <= 1:
return count
occ[best_i] = True
count += 1
def min_M(N):
M = 1
while seated(M) < N:
M += 1
return M
for N in range(2, 13):
formula = N + 2 ** math.ceil(math.log2(N - 1)) if N >= 2 else 1
print(f"N={N:2d}: min M = {min_M(N):2d}, formula = {formula}")
The two columns match for every in the sweep, confirming the boxed expression.
Riddler Classic
What is the fastest way to fill a text editor with one million characters, given these mechanics: tapping a single key produces one character at per second; holding a key produces one character on the first press, waits a second repeat delay, then repeats at characters per second; the keystrokes Ctrl-A, Ctrl-C, right-arrow, Ctrl-V take a combined second from key release to the depress of Ctrl-V and select, copy, and paste the entire buffer; and holding Ctrl-V repeats at the same key rate. How big should the initial buffer be, and how many copy-paste cycles should follow?
The Riddler, FiveThirtyEight, April 27, 2018(original post)
Solution
The two primitives. Let denote the current buffer length. The two actions are:
Holding the key for seconds (with ) appends characters to the buffer: one character on the press, then more after the half-second delay.
Doing a select-copy-paste cycle followed by holding
Ctrl-Vfor seconds (with ) multiplies the buffer by : a -second fixed overhead from key release to the first paste, the first paste adds one copy, and then more held pastes add that many further copies. So the buffer goes from to in time .
The optimisation. A clean policy holds the key once for seconds (producing ), then performs identical copy-paste rounds each multiplying the buffer by in time . The final length is and the total time is . Set .
The per-round log growth rate is . Maximising over by differentiating, which has a unique solution at seconds, where and the log-rate is per second. For an integer number of paste rounds, minimising subject to the length constraint gives the optimum Allowing continuous shaves the bound to seconds; the discrete-round optimum sits a sixth of a second above it. The published winning Riddler entry (Joseph Lombardo) lands on seconds, matching the four-round optimum to the second decimal.
The structure. Both pieces (hold-then-paste-then-paste) obey the same arithmetic. Each copy-paste round costs characters per second in log terms, so the policy is "hold long enough to bootstrap, then paste in identical optimised rounds". The optimum is insensitive to small changes in near ; the curve is flat enough that any human-execution rate near one second per round gets within tenths of a second of the bound.
(The published column notes that solvers using tools outside the stated mechanics, such as Emacs’s prefix argument or Vim macros, did the task in five seconds or fewer. The numbers above answer the puzzle as posed, with the named keystroke primitives.)
The computation
Solve the optimisation directly. For each integer round count , find the paste duration that minimises total time subject to .
For each in a small sweep, define total time where and .
Minimise over by a one-dimensional search.
Return the best triple.
import numpy as np
def time_for(k, p):
m = 30 * p - 13
if m <= 1: return float("inf")
L0 = 1e6 / m ** k
if L0 < 1: return float("inf")
t0 = max(0.5, (L0 + 14) / 30)
return t0 + k * (1 + p)
ps = np.linspace(0.5, 3, 2501)
best = (float("inf"), None, None, None)
for k in range(1, 12):
vals = [time_for(k, p) for p in ps]
i = int(np.argmin(vals))
if vals[i] < best[0]:
m = 30 * ps[i] - 13
L0 = 1e6 / m ** k
best = (vals[i], k, ps[i], L0)
print(
f"optimum: total = {best[0]:.3f} s, "
f"k = {best[1]}, p = {best[2]:.3f} s, L0 = {best[3]:.1f}"
)
The script prints total seconds at , seconds, , reproducing the boxed policy.