Problem 19 — Counting Sundays

solved Thu, 10 Oct 2019, 10:45 · level 2 · difficulty 8% projecteuler.net github

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)?


Problem Analysis

This problem asks us to count how many times the first day of a month landed on a Sunday over a 100-year window. The essential mathematical tool is modular arithmetic applied to the day-of-week cycle. The days of the week form a cycle of length 7: if today is a Monday, then 365 days later is whatever day you get by computing 365 mod 7 = 1 step forward. Because the day of week is inherently cyclic with period 7, we never need to track absolute date counts - only their residues modulo 7. Given a single known anchor date (1 Jan 1900 was a Monday), we can compute the day of week for any future date by accumulating the number of intervening days and reducing modulo 7.

The only subtlety is the Gregorian leap-year rule: a year is a leap year if divisible by 4, except for century years, which must additionally be divisible by 400. This rule decides whether February contributes 28 or 29 days and therefore how many days separate consecutive month boundaries. The problem is tractable because the search space is tiny: the 20th century spans only 1200 months. A simple linear scan over months suffices, giving complexity O(Y x 12) = O(Y) where Y is the number of years in the range - in practice an instantaneous constant-time computation. No combinatorics or dynamic programming is required.

Solution Approach

Day-of-week Simulation (C and Python)

Both files implement the same conceptual algorithm - anchor to a known date and advance month by month - but the C version computes the day-of-week from first principles while the Python version delegates to the standard library.

Step 1 - Establish a reference point. The problem states that 1 January 1900 was a Monday. In the C solution I encode Monday as 0, Tuesday as 1, ..., Sunday as 6. This zero-based scheme makes the modulo-7 arithmetic clean, since the residue is exactly the day index.

Step 2 - Advance to the start year. The count begins in 1901, not 1900. Rather than hard-code the day of week for 1 January 1901, I advance the counter from the known anchor by adding 366 for each leap year and 365 otherwise, reducing modulo 7 at each step. This keeps the code general: it accepts any start year as input.

Step 3 - Iterate over every first-of-month. For each year from start_year to end_year, and for each month 1 through 12, I first check whether the current day-of-week is 6 (Sunday) and increment the count if so, then advance the counter by the number of days in that month. The days_in_month helper encodes the standard month-length table and applies the leap-year correction to February. Crucially, I check before advancing, so dow always represents "the day of week on the 1st of the current month."

Language differences. The C solution does everything manually with loops and a static array. The Python solution lets datetime.date(y, m, 1).isoweekday() do the work - it returns 7 for Sunday, so Python never tracks the day-of-week or the leap-year rule itself. This is more concise and less error-prone, at the cost of a function call per date, which is negligible at this scale. The Python solution sums a generator expression, idiomatically counting matches without building an intermediate list.

workbench

Test cases

categoryinputanswer
main end_year = 2000 start_year = 1901 171

Results

solutioncategoryanswerverdict avg seconds cavg seconds py
p0019_s0 main 171 correct 0.000004 (84) 0.000255 (84)

Files

Tags & blogs

Blogs