The Outbreak Curve
The problem
It is Halloween season, so assume the worst. A zombie virus reaches a town of 80,000 people, carried in by a single infected traveller at hour 0. Nobody is immune, nobody is cured, and there is no way out.
Early on, while almost everyone is still healthy, the number of infected grows exponentially at a rate of r = 0.20 per hour. That is about 22% more each hour, since e^0.2 = 1.221. But the outbreak cannot grow that way forever. The more of the town is already infected, the harder it is for a zombie to find someone new, so the spread slows down. The standard model for this is the logistic equation, where I is the number infected, N the town’s population and r the early growth rate:
dI/dt = r · I · (1 − I/N)
with N = 80,000, r = 0.20 per hour and I = 1 at t = 0. Treat the town as closed (nobody enters or leaves) and the infected as a smooth number rather than a head count.
Emergency services are stretched thinnest at the moment the outbreak is spreading fastest, when new infections per hour are at their maximum. That is when the shelters fill, the beds run out and the barricades are tested hardest.
At what hour, counted from the first infection, is the outbreak spreading fastest?
Notes
- The model is deliberately simple: a closed town, a smooth curve, no one fighting back. A real outbreak is messier. It is the shape of the curve that matters here.
- Answer in hours, to a few decimal places. Converting to hours and minutes is a nice extra.
- It needs a logarithm and one subtraction. Because of that subtraction a slide rule is not a fair contender this time.
Hint
Click for a hint
The rate of new infections, r · I · (1 − I/N), is a parabola in I. It is zero when I = 0 and zero again when I = N, so it peaks exactly half way between them.
The fastest spread therefore happens when I = N/2. Now solve the logistic curve
I(t) = N / ( 1 + (N/I₀ − 1) · e^(−r·t) )
for the time at which I reaches that value.
Solution
Click to reveal the solution
Where the peak is. The rate dI/dt = r · I · (1 − I/N) is a downward parabola in I with its vertex at I = N/2. So new infections per hour are greatest when exactly half the town is infected.
When that happens. The logistic solution is
I(t) = N / ( 1 + (N/I₀ − 1) · e^(−r·t) )
Setting I = N/2 gives 1 + (N/I₀ − 1) · e^(−r·t) = 2, so
(N/I₀ − 1) · e^(−r·t) = 1 → t = ln(N/I₀ − 1) / r
With N = 80,000, I₀ = 1 and r = 0.20:
t = ln(79,999) / 0.20 = 11.28977 / 0.20 = 56.4488 hours
Answer: 56.45 hours, which is 56 hours 27 minutes after the first infection.
Checks. At that moment exactly 40,000 people are infected, and the outbreak is adding r · N / 4 = 0.20 × 80,000 / 4 = 4,000 new infections per hour. Stepping the equation forward numerically in tiny time slices, the rate peaks at the same hour.
Why the logistic matters. Ignore the slowdown and let the infection keep growing exponentially at r = 0.20 per hour, and it reaches 40,000 in ln(40,000) / 0.20 = 52.98 hours. The real curve takes about 3.5 hours longer, because the later part of the climb is already being held back by the shrinking pool of healthy people.
Calculator solutions
HP-42S
No memory registers needed. The subtraction turns 80,000 into 79,999.
80000 ENTER
1 -
LN
0.2 ÷
Result: 56.4488
HP-15C
LN is g then the LN key.
80000 ENTER
1 -
g LN
0.2 ÷
Display: 56.4488
HP-41C
80000 ENTER
1 -
LN
0.2 ÷
Display: 56.4488
HP-48GX
LN is the right-shift of 1/x. Plain stack entry, no symbolic traps.
80000 ENTER
1 -
right-shift 1/x (LN)
0.2 ÷
Display: 56.448847068
HP-25
The machine powers up in FIX 2. Press f FIX 4 first to compare against the other machines.
80000 ENTER
1 -
f LN
0.2 ÷
Display: 56.4488
Elektronika MK-61
Natural log is F then the key marked ln (the 3 key). Enter every number starting with a
digit: 0.2 is typed 0 . 2.
80000 В↑
1 -
F ln
0.2 ÷
Display: 56.448845
The MK-61 works to 8 digits, so its last digit or two can differ slightly from the other machines’.
TI-84
ln(80000-1)/.2
Result: 56.44884707
Casio fx-991
Natural display mode:
ln(80000-1)÷0.2
Result: 56.44884707
Bonus: hold the gate
For programmable calculators only.
The town has one last strongpoint, a walled compound with a single gate. 1,000 zombies are about to hit it. You decide how many defenders to put on the wall. This is a game: you key in a garrison, the program fights the siege, and you learn whether the gate held. Your task is to find the smallest garrison that holds, by writing the program and then playing it.
The rules. The fight is played one round at a time. In every round:
- each defender brings down 0.25 zombies,
- each zombie brings down 0.05 defenders, and
- every defender brought down rises as a zombie, joining the horde at once.
Both sides lose their casualties in the same round, using the numbers from the start of that round. With D defenders and Z zombies, and t = 0.05 · Z defenders falling this round:
D becomes D − t
Z becomes Z + t − 0.25 · D
The fight is over when either side is down to 1 or fewer. If the defenders still have more than one left, the gate held; otherwise it fell. The zombies always start at Z = 1000.
The task. Write a program that takes the garrison D in a register, runs the siege, and stops with the number of defenders left on the display. A result of 1 or less means the gate fell, and a negative number is just the last round overshooting.
Test your program on these cases:
| Case | Garrison D |
|---|---|
| A | 500 |
| B | 600 |
Then play: raise and lower the garrison until you have the smallest whole number of defenders that holds the gate.
Click for a hint
Keep three registers: D, Z and the casualties t. Each round computes t first, then both new numbers from the old ones. The order matters, because the new Z needs the old D:
t = 0.05 · Z
Z = Z + t − 0.25 · D (uses the old D, so do this first)
D = D − t
After each round, test D and then Z against 1 and leave the loop if either is 1 or less. When the loop ends, recall D.
Click to reveal the solution
| Case | Garrison | Defenders left | Result |
|---|---|---|---|
| A | 500 | −1.28 | the gate fell |
| B | 600 | 258.5 | the gate held |
Playing the game, the smallest garrison that holds is 559:
| Garrison | Defenders left | Result |
|---|---|---|
| 558 | −2.07 | the gate fell |
| 559 | 46.7 | the gate held |
Every garrison from 559 upward holds, and every smaller one falls. The edge is sharp: one defender more or fewer decides the siege.
Why 559 and not about 447. If the dead did not rise, this would be the classic square-law fight, named after Frederick Lanchester, who worked it out in the First World War. Firepower scales with the square of the numbers, so the break-even is where 0.25 · D² equals 0.05 · Z²:
D = Z · √(0.05 / 0.25) = 1000 · √0.2 = 447.2
The raised dead add reinforcements to the horde every round, so the defenders need roughly a quarter more than the square law says. The 559 belongs to this model, stepped one round at a time; it is a number for this game, not a constant of nature.
Programs
Programs follow for the HP-42S, HP-15C, HP-41C, Elektronika MK-61 and HP-25. Open the one for your calculator, key it in once, then store a garrison and run it. The text to the right of a listing explains it; do not key it in.
HP-42S
This program uses 29 lines. Press PRGM, key in the lines, press PRGM again.
01 LBL 01 start
02-03 1000 STO 02 Z = 1000, the horde
04 LBL 02 a round starts here
05-08 RCL 02 0.05 × STO 04 t = 0.05 · Z, saved
09 RCL+ 02 Z + t
10-13 RCL 01 0.25 × − Z + t − 0.25 · D, using the old D
14 STO 02 the new Z
15-18 RCL 01 RCL 04 − STO 01 D − t, the new D
19-21 1 X≥Y? GTO 03 1 ≥ D: the defenders are done, leave
22-25 RCL 02 1 X≥Y? GTO 03 1 ≥ Z: the horde is done, leave
26 GTO 02 next round
27 LBL 03
28 RCL 01 the defenders left
29 RTN stop, result in X
To run it, store the garrison and start it. For case A:
500 STO 01
XEQ 01
The defenders left appear in X: −1.2842.
HP-15C
This program uses 42 lines. g P/R toggles program mode; LBL 1 is f LBL 1, and the test is
g x≤y (the g ÷ key).
01 f LBL 1 start
02-06 1000 STO 2 Z = 1000, the horde
07 f LBL 2 a round starts here
08-14 RCL 2 0.05 × STO 4 t = 0.05 · Z, saved
15-16 RCL 2 + Z + t
17-23 RCL 1 0.25 × − Z + t − 0.25 · D, using the old D
24 STO 2 the new Z
25-28 RCL 1 RCL 4 − STO 1 D − t, the new D
29-32 1 ENTER RCL 1 g x≤y D ≤ 1 ?
33 GTO 3 yes: the defenders are done, leave
34-37 1 ENTER RCL 2 g x≤y Z ≤ 1 ?
38 GTO 3 yes: the horde is done, leave
39 GTO 2 next round
40 f LBL 3
41-42 RCL 1 R/S stop with the defenders left showing
To run it, store the garrison and start it. For case A:
500 STO 1
GSB 1
The defenders left show when the program stops: −1.2842. A long fight takes the 15C a few minutes.
HP-41C
This program is 33 lines long. PRGM toggles program mode; LBL is the gold shift of STO, and
the test X<=Y? is the gold shift of +.
01 LBL 01 start
02-03 1000 STO 02 Z = 1000, the horde
04 LBL 02 a round starts here
05-08 RCL 02 0.05 × STO 04 t = 0.05 · Z, saved
09-10 RCL 02 + Z + t
11-14 RCL 01 0.25 × − Z + t − 0.25 · D, using the old D
15 STO 02 the new Z
16-19 RCL 01 RCL 04 − STO 01 D − t, the new D
20-23 1 ENTER↑ RCL 01 X<=Y? D ≤ 1 ?
24 GTO 03 yes: the defenders are done, leave
25-28 1 ENTER↑ RCL 02 X<=Y? Z ≤ 1 ?
29 GTO 03 yes: the horde is done, leave
30 GTO 02 next round
31 LBL 03
32-33 RCL 01 R/S stop with the defenders left showing
To run it, store the garrison and start it. For case A:
500 STO 01
XEQ 01
The defenders left show when the program stops: −1.2842. A long fight takes the 41C a few minutes.
Elektronika MK-61
This program uses 44 of the machine’s 105 steps. F ПРГ enters program mode and F АВТ leaves it.
Type every number starting with a digit. Addresses start at 00. A conditional or БП jump takes its
two-digit address as one step, and the conditional jumps when its test is false.
00-03 1 0 0 0 Z = 1000, the horde
04 x→П 2
05 П→x 2 a round starts here (address 05)
06-11 В↑ 0 . 0 5 × 0.05 · Z
12 x→П 4 t, saved
13-14 П→x 2 + Z + t
15-22 П→x 1 В↑ 0 . 2 5 × − Z + t − 0.25 · D, using the old D
23 x→П 2 the new Z
24-27 П→x 1 П→x 4 − x→П 1 D − t, the new D
28-31 1 В↑ П→x 1 − 1 − D
32-33 F x<0 42 if 1 − D is not negative, D ≤ 1: leave
34-37 1 В↑ П→x 2 − 1 − Z
38-39 F x<0 42 if 1 − Z is not negative, Z ≤ 1: leave
40-41 БП 05 next round
42 П→x 1 the defenders left (address 42)
43 С/П stop
To run it, store the garrison and start it. For case A:
5 0 0 x→П 1
БП 0 0
С/П
The defenders left show when it stops: −1.284218.
The MK-61 works to 8 digits, and the fight subtracts large numbers from each other, so its answer agrees with the other machines to about five figures, not to the last digit. It shows the same winners and losers.
HP-25
The HP-25 has room for 49 program steps, and this program uses 34. With the switch at PRGM, press
f PRGM to clear program memory, then key in the steps. A typed number takes one step per key,
RCL n, STO n and GTO nn take one step each, and a test runs the step after it only when it
is true.
01-05 1 0 0 0 STO 2 Z = 1000, the horde
06 RCL 2 a round starts here (line 06)
07-11 . 0 5 × STO 4 t = 0.05 · Z, saved
12-13 RCL 2 + Z + t
14-19 RCL 1 . 2 5 × − Z + t − 0.25 · D, using the old D
20 STO 2 the new Z
21-24 RCL 1 RCL 4 − STO 1 D − t, the new D
25-27 1 f x≥y GTO 33 1 ≥ D: the defenders are done, leave
28-31 RCL 2 1 f x≥y GTO 33 1 ≥ Z: the horde is done, leave
32 GTO 06 next round
33 RCL 1 the defenders left (line 33)
34 R/S stop
Typing the 1 right after STO 1 pushes D up into Y, so the test asks whether 1 ≥ D with no
RCL needed.
Set the switch to RUN, press f FIX 4, store the garrison, then GTO 0 0 and R/S.
For case A:
500 STO 1
GTO 0 0
R/S
The defenders left show when the program stops: −1.2842. The HP-25 is slow, and a long fight takes it a few minutes.
Solvable on: HP-42S, HP-15C, HP-41C, HP-48GX, HP-25, Elektronika MK-61, TI-84, Casio fx-991