60 Days of Euler in F# - Problem 19

A Confession, Before We Begin

I'm skipping problem 18. I solved it under the 1-minute time limit, but I wasn't happy with it. Problem 67 is the same as problem 18, but with a much larger input. My code ran for a long, long time and never came up with the answer. If you're interest in a really good F# solution to that problem, look here. You can compare it to my non-optimal solution:

let splitBy (c : char) (s : string) = 
    s.Split([|c|], System.StringSplitOptions.RemoveEmptyEntries)

let input = [|
    "75"
    "95 64"
    "17 47 82"
    "18 35 87 10"
    "20 04 82 47 65"
    "19 01 23 75 03 34"
    "88 02 77 73 07 63 67"
    "99 65 04 28 06 16 70 92"
    "41 41 26 56 83 40 80 70 33"
    "41 48 72 33 47 32 37 16 94 29"
    "53 71 44 65 25 43 91 52 97 51 14"
    "70 11 33 28 77 73 17 78 39 68 17 57"
    "91 71 52 38 17 14 91 43 58 50 27 29 48"
    "63 66 04 68 89 53 67 30 73 16 69 87 40 31"
    "04 62 98 27 23 09 70 98 73 93 38 53 60 04 23"
    |]

let lineToArray l =
    l 
    |> splitBy ' ' 
    |> Seq.map int 
    |> Array.ofSeq

let values = input |> Array.map lineToArray
let penRow = values.Length - 2
for y in penRow..(-1)..0 do
    for x in 0..values.[y].Length-1 do
        values.[y].[x] <- values.[y].[x] + 
            max values.[y+1].[x] values.[y+1].[x+1]
values.[0].[0]

So, with that behind us, let's move on.

The Problem

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

The Solution

It may feel like cheating, but I'm not above using the .NET Framework's features to solve these problems. For example, I reduced a couple of problems down to using the BigInteger type. I am learning F# for all the advantages it gives me. I want to learn F# and everything about it. To me, that means not solving problems the language and framework have already solved for me. And that includes dates.

open System

let last = DateTime(2000,12,31)
let first = DateTime(1901,1,1)

let rec countSundays (day : DateTime) total =
    let dayValue() =
        if day.Day = 1 && day.DayOfWeek = DayOfWeek.Sunday
        then 1
        else 0

    if day > last then total
    else
        let next = day.AddDays(1.0)
        let newTotal = total + dayValue()
        countSundays next newTotal

countSundays first 0

The recursive function countSundays takes a day parameter and an accumulator total. When it reaches the last day, it returns the total it has accumulated. Otherwise, it increments the day and adds 1 to the total if the day is a Sunday and also the first day of the month.

Other Posts in This Series