Chapter 25
When Will The Arithmetic Anarchists Attack?
The year is 2000, and an arithmetical anarchist group has an idea. For the next 100 years, it will vandalise a famous landmark whenever the year (in two-digit form, for example this year is “18”) is the product of the month and date, that is, month date year, in MM/DD/YY format. A few questions about the lawless ensuing century: How many attacks happen between the start of 2001 and the end of 2099? Which year sees the most vandalism, and how many years see none? What is the longest gap between attacks?
The Riddler, FiveThirtyEight(original post)
Solution
An attack on date happens exactly when with the month in and the day a real date of that month. So the number of attacks in year is the number of ways to write as with and no larger than the length of month . This is a divisor count, capped by the calendar.
Adding these over gives attacks across the century. The busiest year is : the number factors as , , , , , and , all with valid days, for attacks. At the other extreme, years see no attack at all: every prime above (such as ) and a handful of others (like , whose only small factor needs a nonexistent day) cannot be written as a small month times a real day. The longest drought runs from the single attack of 2057 (March 19, since ) to the first of 2060 (March 20, since ), a gap of days, just over three years.
The computation
Sweep every date of the century: for each year, month and valid day (leap years included), record an attack when month times day equals the two-digit year, then read off the totals, the busiest and quietest years, and the largest gap between consecutive attack dates.
from datetime import date
length = {1:31,2:28,3:31,4:30,5:31,6:30,7:31,8:31,9:30,10:31,11:30,12:31}
attacks = {}; dates = []
for yy in range(1, 100):
n = 0
for m in range(1, 13):
days = length[m] + (1 if yy % 4 == 0 and m == 2 else 0)
for d in range(1, days + 1):
if m * d == yy:
n += 1; dates.append(date(2000 + yy, m, d))
attacks[yy] = n
dates.sort()
gap = max((dates[i+1] - dates[i]).days for i in range(len(dates) - 1))
print(sum(attacks.values()), # 212 total
max(attacks, key=attacks.get), # 24 busiest year
sum(v == 0 for v in attacks.values()), # 20 empty years
gap) # 1097 longest gap