LoginSignup
1
1

More than 3 years have passed since last update.

Project Euler 019を解いてみる。「日曜日の数え上げ」

Last updated at Posted at 2019-09-18

Project Euler 019

019

次の情報が与えられている.
1900年1月1日は月曜日である.
9月, 4月, 6月, 11月は30日まであり, 2月を除く他の月は31日まである.
2月は28日まであるが, うるう年のときは29日である.
うるう年は西暦が4で割り切れる年に起こる. しかし, 西暦が400で割り切れず100で割り切れる年はうるう年でない.
20世紀(1901年1月1日から2000年12月31日)中に月の初めが日曜日になるのは何回あるか?

->次の問題

考え方

力技でやる方法とZellerの公式を用いる方法をやってみました。
設問的にZellerの公式を用いるのはちょっとずるいかな。
設問で1900年1月1日が月曜日と言っていて、数えるのは1901年1月からなのですが、勘違いして少しハマりました(;・∀・)

コード

euler019.py
def main():
    """
    [日, 月, 火, 水, 木, 金, 土]で考える。
    dayの合計が7で割り切れる場合は日曜日
    :return:
    """
    # 1月~12月の日数を入れたリストを作る
    monthes_day_list = [31, ] * 13
    monthes_day_list[0] = 0
    monthes_day_list[2] = 28
    monthes_day_list[4] = 30
    monthes_day_list[6] = 30
    monthes_day_list[9] = 30
    monthes_day_list[11] = 30
    # リストをコピー、2月を29日間にして閏年のリストを作る
    leap_monthes_day_list = monthes_day_list.copy()
    leap_monthes_day_list[2] = 29
    # それぞれの月より前までの日数を合計したリストを作る(その月の1日より以前に何日あるか)
    monthes_day_list = [sum(monthes_day_list[:i]) for i in range(1, len(monthes_day_list) + 1)]
    leap_monthes_day_list = [sum(leap_monthes_day_list[:i]) for i in range(1, len(leap_monthes_day_list) + 1)]
    day = 1  # 1900年1月1日は月曜日から開始
    day = (day + 365) % 7  # 数えるのは1901年からなので365日すすめる。1901年1月1日は火曜日。
    answer = 0
    # 1901年から2000年までループで回す。
    for year in range(1901, 2001):
        temp_monthes_day_list = []
        # 閏年なら閏年のリストを使う
        if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
            temp_monthes_day_list = leap_monthes_day_list
        else:
            temp_monthes_day_list = monthes_day_list
        # その年の初めの曜日(0~6)にそれぞれの月の初めまでの日数を足し、割り切れる(=日曜日)の数をカウント
        answer += sum([((monthes_day + day) % 7 == 0) for monthes_day in temp_monthes_day_list][:-1])
        day += temp_monthes_day_list[-1]
        # 次の年の初めの曜日を出す
        day = day % 7 
    print(answer)


if __name__ == '__main__':
    from time import time as t
    st = t()
    main()
    print(t() - st, 'sec')

paizaにて実行
結果
171
0.0001633167266845703 sec

Zellerの公式を用いる方法

使ったのはこの式です。
Unknown.jpg
コードに入れるならこっちで良かったですね。
$w = y + \lfloor y/4 \rfloor + \lfloor y/400 \rfloor - \lfloor y/100 \rfloor + ( ( 13 * m + 8 ) / 5 + d ) \mod 7 $

コード

euler019.py
def main():
    answer = 0
    for year in range(1901, 2001):  # 1901年~2000年
        for month in range(1, 13):  # 1月~12月
            if weekday(year, month, 1) == 1:  # 各年・月の1日の曜日が日曜(1)に一致すれば
                answer += 1  # 答えのカウントに+1
    print(answer)


def weekday(year: int, month: int, day: int):
    """
    Zellerの公式を用いて曜日を判定する関数
    [0:土, 1:日, 2:月, 3:火, 4:水, 5:木, 6:金]に対応
    :param year: 
    :param month: 
    :param day: 
    :return: 
    """
    if month <= 2:  # 1月・2月は前年の13月・14月として計算する
        month += 12
        year -= 1
    j = year // 100  # jはyearの上2桁
    k = year % 100  # kはyearの下2桁
    h = (day + int(2.6 * (month + 1)) + k + int(k / 4) + int(j / 4) - 2 * j) % 7
    return h

if __name__ == '__main__':
    from time import time as t
    st = t()
    main()
    print(t() - st, 'sec')

paizaにて実行
結果
171
0.0006840229034423828 sec

こっちのほうがスッキリしていいですね。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1