오일러 프로젝트 19

오일러 프로젝트 19번 문제는 20세기에서 매월 1일이 일요일인 경우를 세는 문제이다. 복잡해 보이지만 사실 쉬운 문제이다.

다음은 달력에 관한 몇 가지 일반적인 정보입니다 (필요한 경우 좀 더 연구를 해 보셔도 좋습니다).

  • 1900년 1월 1일은 월요일이다.
  • 4월, 6월, 9월, 11월은 30일까지 있고, 1월, 3월, 5월, 7월, 8월, 10월, 12월은 * 31일까지 있다.
  • 2월은 28일이지만, 윤년에는 29일까지 있다.
  • 윤년은 연도를 4로 나누어 떨어지는 해를 말한다. 하지만 400으로 나누어 떨어지지 않는 매 100년째는 윤년이 아니며, 400으로 나누어 떨어지면 윤년이다

20세기 (1901년 1월 1일 ~ 2000년 12월 31일) 에서, 매월 1일이 일요일인 경우는 총 몇 번입니까?

접근

기준 날짜를 정해놓고 날짜를 하루씩 올려가면서 1일이면서 월요일인 경우를 세는 코드를 작성하면 된다. 이 때 2월은 윤년일 때 29일이 있다. 윤년 여부를 결정하는 것은 위에 설명되어 있는데, 나이브하게 코딩하면

if year % 4 == 0 {
  if year % 100 == 0 {
     if year % 400 == 0 { return true }
     return false
  }
  return true
}

이렇게 중첩되는데, 넓은 범위에서부터 비교하면

  1. 400으로 나눠지면 윤년이다.
  2. 그 와중에 100으로 나눠지면 윤년이 아니다.
  3. 400으로 나눠지지 않는 경우에는 4로 나눠지면 윤년이다.

따라서 다음의 한 줄로 찾을 수 있다.

return (year % 400 == 0) || (year % 100 != 0) && (year % 4 == 0)

그외에 요일은 임의로 정하면 된다. 편의상 0을 일요일, 6을 토요일로 두겠다.

// 1900년 1월 1일은 일요일이다. (문제)
var (year, month, day, weekday) = (1900, 1, 1, 0)
var result = 0
while year < 2001 {
  let days: [Int] = {
    if year % 400 == 0 || year % 100 != 0 && year % 4 == 0 {
      return [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    }
    return [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
  }()
  /// 조건에 맞으면 카운트를 1올린다.
  /// 20세기는 1901년부터 2000년이라는 점을 잊지말자
  if (1901...2000) ~= year, day == 1, weekday == 0 { result += 1 }
  /// 다음날로 이동한다.
  (day, weekday) = (day + 1, (weekday + 1) % 7)
  if day > days[month-1] {
    (day, month) = (1, month + 1)
    if month > 12 {
      (month, year) = (1, year + 1)
    }
  }
}
print(result)

파이썬 풀이

풀이 패턴 자체가 매우 단순하기 때문에 파이썬으로 같은 코드를 쓰는 것보다는 다른 풀이를 쓰고자 한다. datetime 모듈의 datetime 은 두 날짜간의 거리를 계산할 수 있는데, 이 때 쓰이는 값이 timedelta 객체이다. 반대로 datetime 에 timedelta 객체를 더하면 그 날이 지난 날짜를 얻을 수 있다.1 이를 이용해서 계산해보자. 참고로 weekday는 다른 날짜 속성과는 달리 메소드이므로 호출해서 값을 얻어야 한다.

import datetime

def solve():
  ## 1900-01-01을 참조할 필요없이 20세기 첫날부터 그냥 시작한다.
  t = datetime.datetime(1901, 1, 1)
  d = datetime.timedelat(days=1)
  result = 0
  while t.year < 2001:
    if t.day == 0 and t.weekday() == 0:
      result += 1
    t += d
  print(result)

 


  1. 이 때 사용되는 방식은 그냥 덧셈(+)이다.