Rubyで20世紀の日曜日を求める 〜Rubyでオイラープロジェクトを解こう!Problem19

Problem 19 - Project Eulerより

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)?
以下の情報が与えられているが、あなたは更なる調査を欲している。

  • 1900年1月1日は月曜日である。
  • 9月、4月、6月および11月は30日である。
  • 2月を別にして残りは31日である。
  • 2月は28日で、うるう年には29日である。
  • うるう年は、4で割り切れる年に起こるが、400で割れる年を除くと世紀の年には起こらない。

20世紀(1901年1月1日から2000年12月31日まで)における月の初日が日曜日である日はいくつあるか。


方針:

  • 入力日の曜日を返すwdayメソッドを作る
  • sundays_on_first_of_monthメソッドで20世紀のすべての月の初日の曜日を当たる
 MONTHS = %w(nil Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)
 MONTH_DAYS = (1..12).inject({}) { |h, m| [4,6,9,11].include?(m) ? h[m] = 30 : h[m] = 31; h}

 def wday(day, mon, year)
   days = 0
   mon = month_index(mon)
   (1900..year).each do |y|
     leap_year?(y) ? MONTH_DAYS[2] = 29 : MONTH_DAYS[2] = 28
     if y != year
       (1..12).each { |m| days += MONTH_DAYS[m] }
     else
       (1...mon).each { |m| days += MONTH_DAYS[m] }
       days += day
     end
   end
   days % 7 #return day of week: 0:sun, 1:mon..
 end

 def leap_year?(year)
   if (year%4).zero? && (year%100).nonzero? || (year%400).zero?
     true
   else
     false
   end
 end

 def sundays_on_first_of_month(start_date, end_date)
   st_day, st_mon, st_year = start_date
   end_day, end_mon, end_year = end_date
   st_mon, end_mon = month_index(st_mon), month_index(end_mon)
   count = 0
   (st_year..end_year).each do |y|
     cnt_blk = lambda { |m| count += 1 if wday(1, MONTHS[m], y).zero? } # count Sunday
     if y == st_year and y == end_year
       (st_mon..end_mon).each(&cnt_blk)
     elsif y == st_year
       (st_mon..12).each(&cnt_blk)
     elsif y == end_year
       (1..end_mon).each(&cnt_blk)
     else
       (1..12).each(&cnt_blk)
     end
   end
   count
 end

 def month_index(word) # from month word to month number
   MONTHS.find_index { |m| m =~ /#{word.slice(0..2)}/i }
 end

 wday(1, 'january', 1900) # => 1
 sundays_on_first_of_month([1, 'Jan', 1901],[31, 'Dec', 2000]) # => 1xx

答えはでたけど
効率悪そうです