# Project Euler # 19 Counting Sundays in Python

You are given the following information, but you may prefer to do some research for yourself.

1 Jan 1900 was a Monday.
Thirty days has September,
April, June and November.
All the rest have thirty-one,
Saving February alone,
Which has twenty-eight, rain or shine.
And on leap years, twenty-nine.
A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.

How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?

day_names = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
month_names = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September',
'October', 'November', 'December']
month_days_common = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
month_to_days = dict(zip(month_names, month_days_common))

def is_leap(year):
"""return True if a year is leap. False otherwise."""
if (year % 4) == 0:
if (year % 100) == 0:
if (year % 400) == 0:
return True
else:
return False
else:
return True
else:
return False

def get_date_day_names(end_year):
"""return a list of tuples containing day name, day, month, year from 1900 to end_year(exclusive)."""
start_year = 1900
total = 0
all_day_names = []
all_months1 = []
all_months2 = {}
year_month_times_number_of_days = []
final_dates = []
for year in range(start_year, end_year):
count = 0
while count < 12:
all_months1.append((year, month_names[count]))
count += 1
for year in range(start_year, end_year):
if is_leap(year):
total += 366
else:
total += 365
for i in range(total):
all_day_names.append(day_names[i % 7])
for year, month in all_months1:
if is_leap(year):
if month == 'February':
all_months2[(year, month)] = 29
else:
all_months2[(year, month)] = month_to_days[month]
else:
all_months2[(year, month)] = month_to_days[month]
for date, number_of_days in all_months2.items():
for i in range(number_of_days):
year_month_times_number_of_days.append((date, i + 1))
date_day_name = list(zip(all_day_names, year_month_times_number_of_days))
for date_item in date_day_name:
final_dates.append((date_item[0], date_item[1][1], date_item[1][0][1], date_item[1][0][0]))
return final_dates

def count(day_name, day_of_the_month, end_year):
"""returns count of a given day from 1901 to end_year(exclusive)"""
final_dates = []
dates_1900_to_end_year = get_date_day_names(end_year)
for date in dates_1900_to_end_year:
if date[-1] != 1900 and date[0] == day_name and date[1] == day_of_the_month:
final_dates.append(date)
return len(final_dates)

if __name__ == '__main__':
print(count('Sunday', 1, 2001))


# whole algorithmic change => $$\O(n)\$$

Your existing algorithm is quite confusing. Perhaps instead you could try a simpler structure such as:

from datetime import date

def num_sundays_on_first_of_month(year1, year2):
num_sundays = 0
for i in range(year1, year2+1):
for j in range(1, 13):
if date(i, j, 1).weekday() == 6:
num_sundays += 1
return num_sundays

if __name__ == '__main__':
print(num_sundays_on_first_of_month(1901,2000))


This code is much more readable and far briefer. It makes effective use of the datetime module and its date objects. It works by looping through every month between year1 and year2 inclusive and counting if it is a Sunday.

This runs at $$\O(n)\$$ time where $$\n = year2 - year1\$$

• Yeah, you have a point, I sometimes overcomplicate stuff. The whole point was trying not to use modules and I did it for fun I guess. – user203258 Jul 19 '19 at 9:48
• If you don't. Want to use modules, The date class is a very simple object that can easily be added to your program. – Matthew Anderson Jul 19 '19 at 23:55