Instantly share code, notes, and snippets.

# estranged42/arithmetical-anarchist.pySecret Created Apr 6, 2018

 """ https://fivethirtyeight.com/features/when-will-the-arithmetic-anarchists-attack/ From Eric Veneto, mathematical madmen are on the loose: The year is 2000, and an arithmetical anarchist group has an idea. For the next 100 years, it will vandalize a famous landmark whenever the year (in two-digit form, for example this year is “18”) is the product of the month and date (i.e. month × date = year, in the MM/DD/YY format). A few questions about the lawless ensuing century: How many attacks will happen between the beginning of 2001 and the end of 2099? What year will see the most vandalism? The least? What will be the longest gap between attacks? Run this script with: ``` \$ python3 arithmetical-anarchist.py ``` Expected Output: How many attacks will happen between the beginning of 2001 and the end of 2099? 212 What year will see the most vandalism? 2024 had 7 attacks. What year will see the least vandalism? 2037,2041,2043,2047,2053,2058,2059,2061,2062,2067,2071,2073,2074,2079,2082,2083,2086,2089,2094,2097 had zero attacks. 2001,2013,2017,2019,2023,2029,2031,2034,2038,2039,2046,2049,2051,2057,2065,2068,2069,2076,2085,2087,2091,2092,2093,2095,2098 had one attack. What will be the longest gap between attacks? 1096 days between 3/19/2057 and 3/20/2060. """ # Setup our date range. years = range(1,100) months = range(1,13) days_in_months = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] # Keep track of all attack events and longest gap between attacks. events = {} gap_start = "1/1/2001" gap_end = "1/1/2001" gap_len = 0 days_since_attack = 0 # Convert m d y integers to a date string. def format_attack_date(d, m, y): return "{:d}/{:d}/{:s}".format(m, d, format_year(y)) # Convert a 1 or 2 digit year to a 4 digit year string. def format_year(y): if y < 10: return "200" + str(y) else: return "20" + str(y) # Brute force all days in all years in all months for y in years: for m in months: num_days = days_in_months[m-1] # Watch out for off-by-one errors # Look out for leap years if m == 2 and (y % 4) == 0: num_days = 29 for d in range(1, num_days+1): if d * m == y: # There's an attack on this day # See if this beats the record for longest gap between attacks if days_since_attack > gap_len: gap_start = last_attack gap_end = format_attack_date(d, m, y) gap_len = days_since_attack # Keep track of the last attack (which is now this one) last_attack = format_attack_date(d, m, y) # Attack happened today, so reset days_since_attack days_since_attack = 0 # Store this attack in our events table if y not in events: events[y] = {} if m not in events[y]: events[y][m] = [] events[y][m].append(d) else: # No attack today days_since_attack += 1 # Calculate the best and worst years worst_years = [] worst_count = 0 best_years = [] one_years = [] event_total = 0 for y in years: events_this_year = 0 if y in events.keys(): # At least one attack happened this year for m in events[y].keys(): # Add this month's attacks to the year total events_this_year += len(events[y][m]) else: # No attacks happened this year best_years.append(format_year(y)) # Does this year tie with the current worst year? if events_this_year == worst_count: worst_years.append(format_year(y)) # Is this year the new worst year? if events_this_year > worst_count: worst_count = events_this_year worst_years = [format_year(y)] # If there was only one attack this year, keep track of it if events_this_year == 1: one_years.append(format_year(y)) # Add up all the events this year event_total += events_this_year print("How many attacks will happen between the beginning of 2001 and the end of 2099?") print(event_total) print() print("What year will see the most vandalism?") print("{:s} had {:d} attacks.".format(",".join(worst_years), worst_count) ) print() # This question was a bit ambiguous to me. There are years where no attacks take place, # so are those the years with the least? Or are we only looking for years where an # attack took place? I fudged and lists all years with zero or one attack. print("What year will see the least vandalism?") print("{:s} had zero attacks.".format(",".join(best_years)) ) print("{:s} had one attack.".format(",".join(one_years)) ) print() print("What will be the longest gap between attacks?") print("{:d} days between {:s} and {:s}.".format(gap_len, gap_start, gap_end) ) print()

### tmsgh commented Apr 14, 2018 • edited

 Here is a shorter version: ``````# https://fivethirtyeight.com/features/when-will-the-arithmetic-anarchists-attack/ from statistics import mode;t=[31,28,31,30,31,30,31,31,30,31,30,31];a=[[d,m,y] for y in range(1,100) for m in range(1,13) for d in range(1,32) if d<=(t[m-1]+(not y%4 and m==2))];b=[n for n in a if n[2]==n[0]*n[1]];c=[n[2] for n in b];d=[n[0] for n in enumerate(a) if n[1] in b];print(len(b)," attacks; most attacks in",mode(c),"; safest years=",[n for n in range(1,100) if n not in c],"; longest gap = ",max([d[n+1]-d[n] for n in range(len(d)-1)])-1," days") ``````
to join this conversation on GitHub. Already have an account? Sign in to comment