Here’s problem nineteen:
Question: How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?
This could’ve been solved trivially by using the Python date module, but where’s the fun in that? I decided to create my own date-incrementer by simply the date in a list, as [month, day, year, weekday], and incrementing by day using a handful of functions.
The code is long, but we’ll look at it in chunks:
def leapyear(date): # checks if year is leap year global leapyr if str(date[2])[-2:] != '00' and date[2] % 4 == 0: leapyr = True elif str(date[2])[-2:] == '00' and date[2] % 400 == 0: leapyr = True else: leapyr = False def weekday(date): # increments weekday (m, t, w..) if date[3] == 7: date[3] = 1 else: date[3] += 1 return date def day(date): # increments day (1, 2, 3, 4..31) if any(date[0] == i for i in [4, 6, 9, 11]): # 30 days if date[1] == 30: date[1] = 1 else: date[1] += 1 elif date[0] == 2: # february if leapyr: if date[1] == 29: date[1] = 1 else: date[1] += 1 else: if date[1] == 28: date[1] = 1 else: date[1] += 1 else: # 31 days if date[1] == 31: date[1] = 1 else: date[1] += 1 return date def month(date): # increments month if date[1] == 1: # since we increment the day first, if the day is 1, meaning the first day of a new month if date[0] != 12: # increment month date[0] += 1 else: date[0] = 1 return date def year(date): # increments year if date[1] == 1 and date[0] == 1: # we increment day and month first, so if it is jan. 1st date[2] += 1 # increment year return date date = [1, 1, 1901, 2] # start date, is tuesday result = 0 # holds number of sundays on first of month while date[0:3] != [12, 31, 2000]: # up to this date leapyear(date) date = weekday(date) date = day(date) date = month(date) date = year(date) if date[1] == 1 and date[3] == 7: result += 1 print(result)
So let’s look at the run code first, at the very bottom.
date = [1, 1, 1901, 2] # start date, is tuesday result = 0 # holds number of sundays on first of month while date[0:3] != [12, 31, 2000]: # up to this date leapyear(date) date = weekday(date) date = day(date) date = month(date) date = year(date) if date[1] == 1 and date[3] == 7: result += 1 print(result)
The starting date is stored as a list in date, and is a Tuesday (found by WolframAlpha), hence the 2. While the date is not the ending date (Dec 31, 2000), we run this chunk of code. leapyear(date) checks to see if the year is a leap year, and the next four functions each increment the date according to its name. Then we check if the current date is a Sunday on the first of a month, and if it is, we add one to result.
def leapyear(date): # checks if year is leap year global leapyr if str(date[2])[-2:] != '00' and date[2] % 4 == 0: leapyr = True elif str(date[2])[-2:] == '00' and date[2] % 400 == 0: leapyr = True else: leapyr = False
Now let’s look at leapyear(). It calculate leap years based on what the question told us: “A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400”. We check if the last two digits of the year are “00” to determine if a year is a century. This is pretty simple.
def weekday(date): # increments weekday (m, t, w..) if date[3] == 7: date[3] = 1 else: date[3] += 1 return date
Incrementing the weekday is also pretty simple, and we use Monday as 1 up to Sunday as 7. If it’s Sunday, the next day is Monday, so we make sure to reset the weekday to 1.
def day(date): # increments day (1, 2, 3, 4..31) if any(date[0] == i for i in [4, 6, 9, 11]): # 30 days if date[1] == 30: date[1] = 1 else: date[1] += 1 elif date[0] == 2: # february if leapyr: if date[1] == 29: date[1] = 1 else: date[1] += 1 else: if date[1] == 28: date[1] = 1 else: date[1] += 1 else: # 31 days if date[1] == 31: date[1] = 1 else: date[1] += 1 return date
Here it gets a bit complicated. We have to know what month it is to calculate the number of days in that month, and also if it’s a leap year. The code is divided into three chunks, with the first and last dedicated to months with 30 and 31 days, respectively. This is more or less straightforward, using the same logic for incrementing as in weekday(). For February, we have to consider if it’s a leap year or not. This is found by the global variable leapyr, which we calculate at every date by the function leapyear(). If it is a leap year, we increment the day until it hits 29, and if it isn’t, we increment until 28.
def month(date): # increments month if date[1] == 1: # since we increment the day first, if the day is 1, meaning the first day of a new month if date[0] != 12: # increment month date[0] += 1 else: date[0] = 1 return date
Moving on to the month. Remember that in the run code that we increment the day before we increment the month. So, that means if the day is 1, it must be the first day of a new month, and so we increment the month by one. Same logic as before, incrementing up to 12.
def year(date): # increments year if date[1] == 1 and date[0] == 1: # we increment day and month first, so if it is jan 1st date[2] += 1 # increment year return date
The last part is incrementing the year. We increment the day and month before incrementing the year, so if the day and month are 1/1 (Jan 1st), that means it must be a new year, so we increment year by 1. There is no limit on how high the year can go.
When the date reaches Dec 31, 2000, the loop ends and we print the result.
Answer: 171