Chapter 189
How Fast Can You Deliver PB&Js? How Many Meerkats Can Survive?
Riddler Express
You deliver peanut-butter-and-jelly sandwiches from a restaurant at th Street and Avenue F in Riddler City. The grid has east-west streets (st to st) and north-south avenues (A to U), with each block miles long. The speed limit is mph everywhere, except Avenue U (the Ultra-Speed Trafficway) at mph. For which destinations does it pay to detour to Avenue U? (No traffic, no turn losses.)
The Riddler, FiveThirtyEight, July 6, 2018(original post)
Solution
Index streets and avenues (so A , F , U ). Each block is miles. At mph a block takes minutes; on Avenue U at mph a block takes minutes. You start at .
Direct route. Drive Manhattan-style on surface streets. Time
Avenue-U route. Drive east to Avenue U ( blocks at mph), then north or south along U ( blocks at mph), then west back to avenue ( blocks at mph). Time (If you skip the westward leg.)
Avenue U pays whenever : which rearranges to The right-hand side is a function of alone; call it the distance-from-20th threshold . The pattern is:
| avenue | avenue letter | |
|---|---|---|
| to | A to F | |
| G | ||
| H | ||
| I | ||
| J | ||
| K | ||
| L | ||
| M | ||
| N | ||
| O | ||
| P | ||
| Q | ||
| R | ||
| S | ||
| T | ||
| U |
Read this geometrically. Streets only go up to , that is blocks north of you and blocks south. So for A, B, C, D, E, F (the western strip) Avenue U pays only for destinations more than blocks away from th Street, that is for street numbers above rd to the north (south is impossible: only blocks down). Moving east, the threshold drops linearly: by Avenue M () you save by detour for anything more than blocks away, that is street or street . By Avenue T () you save almost everywhere off your home row.
The computation
Encode the grid, compute both route times for every address, and confirm the boundary by exhaustive enumeration.
def t_direct(r, c):
return 0.3 * (abs(r - 20) + abs(c - 6))
def t_via_U(r, c):
if c == 21:
return 0.3 * 15 + 0.03 * abs(r - 20)
return 0.3 * 15 + 0.03 * abs(r - 20) + 0.3 * (21 - c)
faster = 0
boundary = {}
for r in range(1, 62):
for c in range(1, 22):
if (r, c) == (20, 6):
continue
if t_via_U(r, c) < t_direct(r, c):
faster += 1
for c in range(1, 22):
boundary[c] = min(
(abs(r - 20) for r in range(1, 62)
if t_via_U(r, c) < t_direct(r, c)),
default=None,
)
print("addresses where Avenue U is faster:", faster)
print("boundary |r-20| per column (None = never):")
for c in range(1, 22):
print(f" c={c:2d} ({chr(ord('A')+c-1)}): {boundary[c]}")
The script prints addresses where Avenue U is faster: 532 (out of ) and the per-avenue boundary , matching the analytic thresholds above. The qualitative answer: use Avenue U for deliveries that are far north (and slightly far south on the eastern side); stay on surface streets near home.
Riddler Classic
Meerkats eat scorpions. Without meerkats the scorpion population doubles every month; without scorpions the meerkat population halves every month; at scorpions and meerkats both populations are stable. Use the Lotka-Volterra model for predator and prey.
(a) How many meerkats are there when the scorpion population is at its maximum? (b) How many of each are there when the meerkat population is increasing by per month and the scorpion population is decreasing by per month? (c) Starting from scorpions and meerkats, what is the largest the meerkat population gets, and how long until both populations return to their starting values?
The Riddler, FiveThirtyEight, July 6, 2018(original post)
Solution
Let be the scorpion population, the meerkat population, time in months. The Lotka-Volterra equations are
Fitting parameters. With : , so . Doubling each month means , hence . With : , so halves each month means , hence . The equilibrium at gives and , so
(a) Maximum scorpions. Along any trajectory reaches a maximum when , that is when , that is when By symmetry the meerkat maximum sits at scorpions.
(b) The prescribed rates. Set and : From the first, , so . From the second, , so . Substituting and solving numerically gives (Fractional animals are an artefact of the continuous model.)
(c) Extra credit: orbit from . Lotka-Volterra trajectories are closed orbits around the equilibrium , with conserved quantity The starting point fixes and the orbit. The peak meerkat population on the orbit is found by setting along the orbit () and using . Numerically integrating gives meerkats and the orbit period months. Both values are reproduced in the computation block.
The computation
Integrate the Lotka-Volterra ODE numerically with the fitted parameters, read off the equilibrium conditions in (a) and (b), and measure the orbit in (c).
import math
import numpy as np
from scipy.integrate import odeint
from scipy.optimize import fsolve
a = math.log(2)
c = math.log(2)
b = a / 5
d = c / 20
# (b) solve for (x, y) given dx/dt = -2, dy/dt = +4
def eqs(v):
x, y = v
return [x * (a - b * y) + 2, y * (d * x - c) - 4]
xb, yb = fsolve(eqs, [40, 5])
print(f"(b) scorpions={xb:.3f}, meerkats={yb:.3f}")
# (c) orbit from (100, 10)
def rhs(state, t):
x, y = state
return [a * x - b * x * y, d * x * y - c * y]
t = np.linspace(0, 30, 200_000)
sol = odeint(rhs, [100, 10], t)
xs, ys = sol[:, 0], sol[:, 1]
print(f"(c) max meerkats = {ys.max():.3f}")
# Period: return to (100, 10)
dist = np.sqrt((xs - 100) ** 2 + (ys - 10) ** 2)
i = np.argmin(dist[len(t) // 10:]) + len(t) // 10
print(f"(c) orbit period = {t[i]:.3f} months")
The script prints , , and orbit period months, matching the boxed answers.