Python で13日の金曜日の頻度を調べる

授業「コンピュータ演習」の2023年,年初めの練習問題。2023年1月13日金曜日。これを記念したスペシャル問題の解答例。

Python の calendar を使う

calendar を import して,月のカレンダーを出力します。

In [1]:
import calendar
In [2]:
# 2023年1月の月カレンダーを出力
calendar.prmonth(2023, 1)
# ヨーロッパの慣習に従い,月曜日始まり
    January 2023
Mo Tu We Th Fr Sa Su
                   1
 2  3  4  5  6  7  8
 9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31

Python の locale で曜日を日本語に

既定では,英語表記で週のはじまりが月曜日(Mo)になっています。これを日本語表記にし,週のはじまりを日曜日にするには,以下のようにします。(曜日が日本語表示になると,レイアウトが崩れてしまいます。)

In [3]:
import locale

# locale を日本語に設定。これ以後は日本語表示に。
locale.setlocale(locale.LC_TIME, 'ja_JP.UTF-8')

# locale を 日本語に設定した後の「曜日」
print(calendar.day_abbr[:])
['月', '火', '水', '木', '金', '土', '日']
In [4]:
# 週の始まりを日曜日に
calendar.setfirstweekday(calendar.SUNDAY)

calendar.prmonth(2023, 1)

# レイアウトが崩れる...
# 曜日表示の間のスペースが1個多い...
      1月 2023
日  月  火  水  木  金  土
 1  2  3  4  5  6  7
 8  9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31

参考: %%bashcal コマンド

弘大 JupyterHub は Linux ですから,JupyterHub のホームの「新規」から「端末」を選ぶと,bash のプロンプトに続いてコマンドを打ち込むことができます。

わざわざ「新規」から「端末」を選ばなくても,Python のノートブックであれば,以下のように「セル」の1行目に %%bash と書いたあとにコマンドを入力して「▶︎ Run」(Shift Enter)すると,結果が表示されます。こちらは,日本語表示でもレイアウトの崩れはありません。

In [5]:
%%bash

# 2023 年 1 月のカレンダーを表示する
cal 1 2023
      1月 2023         
日 月 火 水 木 金 土  
 1  2  3  4  5  6  7  
 8  9 10 11 12 13 14  
15 16 17 18 19 20 21  
22 23 24 25 26 27 28  
29 30 31              
                      
In [6]:
%%bash

# 2023 年の年間カレンダーを表示する
cal 2023
                            2023
         1月                    2月                    3月           
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土  
 1  2  3  4  5  6  7            1  2  3  4            1  2  3  4  
 8  9 10 11 12 13 14   5  6  7  8  9 10 11   5  6  7  8  9 10 11  
15 16 17 18 19 20 21  12 13 14 15 16 17 18  12 13 14 15 16 17 18  
22 23 24 25 26 27 28  19 20 21 22 23 24 25  19 20 21 22 23 24 25  
29 30 31              26 27 28              26 27 28 29 30 31     
                                                                  

         4月                    5月                    6月           
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土  
                   1      1  2  3  4  5  6               1  2  3  
 2  3  4  5  6  7  8   7  8  9 10 11 12 13   4  5  6  7  8  9 10  
 9 10 11 12 13 14 15  14 15 16 17 18 19 20  11 12 13 14 15 16 17  
16 17 18 19 20 21 22  21 22 23 24 25 26 27  18 19 20 21 22 23 24  
23 24 25 26 27 28 29  28 29 30 31           25 26 27 28 29 30     
30                                                                

         7月                    8月                    9月           
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土  
                   1         1  2  3  4  5                  1  2  
 2  3  4  5  6  7  8   6  7  8  9 10 11 12   3  4  5  6  7  8  9  
 9 10 11 12 13 14 15  13 14 15 16 17 18 19  10 11 12 13 14 15 16  
16 17 18 19 20 21 22  20 21 22 23 24 25 26  17 18 19 20 21 22 23  
23 24 25 26 27 28 29  27 28 29 30 31        24 25 26 27 28 29 30  
30 31                                                             

        10月                   11月                   12月           
日 月 火 水 木 金 土  日 月 火 水 木 金 土  日 月 火 水 木 金 土  
 1  2  3  4  5  6  7            1  2  3  4                  1  2  
 8  9 10 11 12 13 14   5  6  7  8  9 10 11   3  4  5  6  7  8  9  
15 16 17 18 19 20 21  12 13 14 15 16 17 18  10 11 12 13 14 15 16  
22 23 24 25 26 27 28  19 20 21 22 23 24 25  17 18 19 20 21 22 23  
29 30 31              26 27 28 29 30        24 25 26 27 28 29 30  
                                            31                    

Python の datetime を使う

日付や曜日を扱う datetime を import して,曜日を調べてみます。

In [7]:
import datetime as dt

問題 1

2023年の1月から12月までの13日の曜日を全て表示せよ。

In [8]:
year = 2023
day = 13
for month in range(1, 13):
    date = dt.date(year, month, day)
    youbi = calendar.day_abbr[date.weekday()]
    print('%4d%2d%2d日は %s曜日' % (year, month, day, youbi))
2023年 1月13日は 金曜日
2023年 2月13日は 月曜日
2023年 3月13日は 月曜日
2023年 4月13日は 木曜日
2023年 5月13日は 土曜日
2023年 6月13日は 火曜日
2023年 7月13日は 木曜日
2023年 8月13日は 日曜日
2023年 9月13日は 水曜日
2023年10月13日は 金曜日
2023年11月13日は 月曜日
2023年12月13日は 水曜日

問題 2

2023年の1月から12月まで,13日が金曜日である月のみを表示せよ。

In [9]:
year = 2023
day = 13
print('%2d日が 金曜日なのは' % day)
for month in range(1, 13):
    date = dt.date(year, month, day)
    youbi = calendar.day_abbr[date.weekday()]
    if youbi == '金':
        print('    %4d%2d月' % (year, month))
13日が 金曜日なのは
    2023年 1月
    2023年10月

問題 3

2023年から2122年までの100年間に,13日の金曜日は1年あたり平均何回あるか。

In [10]:
year0 = 2023
day = 13
count = 0
print('%2d日が金曜日なのは' % day, end='')
for year in range(year0, year0+100):
    # 年ごとに改行
    print('')
    for month in range(1, 13):
        date = dt.date(year, month, day)
        youbi = calendar.day_abbr[date.weekday()]
        if youbi == '金':
            # 同一年は改行せずに横並びで表示
            print('  %4d%2d月' % (year, month), end='')
            count += 1
print('')
print('')
print('100年間で %3d回,年平均 %.2f回' % (count, count/100))
13日が金曜日なのは
  2023年 1月  2023年10月
  2024年 9月  2024年12月
  2025年 6月
  2026年 2月  2026年 3月  2026年11月
  2027年 8月
  2028年10月
  2029年 4月  2029年 7月
  2030年 9月  2030年12月
  2031年 6月
  2032年 2月  2032年 8月
  2033年 5月
  2034年 1月  2034年10月
  2035年 4月  2035年 7月
  2036年 6月
  2037年 2月  2037年 3月  2037年11月
  2038年 8月
  2039年 5月
  2040年 1月  2040年 4月  2040年 7月
  2041年 9月  2041年12月
  2042年 6月
  2043年 2月  2043年 3月  2043年11月
  2044年 5月
  2045年 1月  2045年10月
  2046年 4月  2046年 7月
  2047年 9月  2047年12月
  2048年 3月  2048年11月
  2049年 8月
  2050年 5月
  2051年 1月  2051年10月
  2052年 9月  2052年12月
  2053年 6月
  2054年 2月  2054年 3月  2054年11月
  2055年 8月
  2056年10月
  2057年 4月  2057年 7月
  2058年 9月  2058年12月
  2059年 6月
  2060年 2月  2060年 8月
  2061年 5月
  2062年 1月  2062年10月
  2063年 4月  2063年 7月
  2064年 6月
  2065年 2月  2065年 3月  2065年11月
  2066年 8月
  2067年 5月
  2068年 1月  2068年 4月  2068年 7月
  2069年 9月  2069年12月
  2070年 6月
  2071年 2月  2071年 3月  2071年11月
  2072年 5月
  2073年 1月  2073年10月
  2074年 4月  2074年 7月
  2075年 9月  2075年12月
  2076年 3月  2076年11月
  2077年 8月
  2078年 5月
  2079年 1月  2079年10月
  2080年 9月  2080年12月
  2081年 6月
  2082年 2月  2082年 3月  2082年11月
  2083年 8月
  2084年10月
  2085年 4月  2085年 7月
  2086年 9月  2086年12月
  2087年 6月
  2088年 2月  2088年 8月
  2089年 5月
  2090年 1月  2090年10月
  2091年 4月  2091年 7月
  2092年 6月
  2093年 2月  2093年 3月  2093年11月
  2094年 8月
  2095年 5月
  2096年 1月  2096年 4月  2096年 7月
  2097年 9月  2097年12月
  2098年 6月
  2099年 2月  2099年 3月  2099年11月
  2100年 8月
  2101年 5月
  2102年 1月  2102年10月
  2103年 4月  2103年 7月
  2104年 6月
  2105年 2月  2105年 3月  2105年11月
  2106年 8月
  2107年 5月
  2108年 1月  2108年 4月  2108年 7月
  2109年 9月  2109年12月
  2110年 6月
  2111年 2月  2111年 3月  2111年11月
  2112年 5月
  2113年 1月  2113年10月
  2114年 4月  2114年 7月
  2115年 9月  2115年12月
  2116年 3月  2116年11月
  2117年 8月
  2118年 5月
  2119年 1月  2119年10月
  2120年 9月  2120年12月
  2121年 6月
  2122年 2月  2122年 3月  2122年11月

100年間で 173回,年平均 1.73回

問題 4

13日の金曜日は1年に必ず1回以上あることを示せ。

ヒント:2023年のように,1月1日が日曜日の場合は,1月13日が金曜日となる。では,1月1日が他の曜日の場合はどうなるか。残りの曜日の場合を調べればよいことになる。

例えば,1月1日が月曜日なら,各月末までの日数の和を7で割った余りが6なら,次の月初めが日曜日ということになる。

In [11]:
# 閏年でない場合
# 各月の日数
mdays = [[1, 31], [2, 28], [3, 31], [4, 30], [5, 31], [6, 30], 
         [7, 31], [8, 31], [9, 30], [10,31], [11,30], [12,31]]
print('閏年でない場合')
for weekday in range(7):
    print('  1月1日が「%s」曜日なら... ' % calendar.day_abbr[weekday], end=' ')
    if (calendar.day_abbr[weekday]) == '日':
        print(' 1月1日が日曜日', end='  ')
    tmp = 0
    for i in range(11):
        tmp += mdays[i][1]
        if tmp % 7 == 6 - weekday:
            print('%2d月1日が日曜日' % mdays[i+1][0], end='  ')
    print('')

print('')

# 閏年の場合
# 各月の日数
mdays = [[1, 31], [2, 29], [3, 31], [4, 30], [5, 31], [6, 30], 
         [7, 31], [8, 31], [9, 30], [10,31], [11,30], [12,31]]
print('閏年の場合')
for weekday in range(7):
    print('  1月1日が「%s」曜日なら... ' % calendar.day_abbr[weekday], end=' ')
    if (calendar.day_abbr[weekday]) == '日':
        print(' 1月1日が日曜日', end='  ')
    tmp = 0
    for i in range(11):
        tmp += mdays[i][1]
        if tmp % 7 == 6 - weekday:
            print('%2d月1日が日曜日' % mdays[i+1][0], end='  ')
    print('')
    
print('')
print('したがって,閏年か否かにかかわらず')
print('1月1日が日〜土のいかなる曜日で始まっても')
print('その年には必ず1回以上,月初めの1日(ついたち)が日曜日となる月があり,')
print('その月の13日が金曜日となる。')
閏年でない場合
  1月1日が「月」曜日なら...   4月1日が日曜日   7月1日が日曜日  
  1月1日が「火」曜日なら...   9月1日が日曜日  12月1日が日曜日  
  1月1日が「水」曜日なら...   6月1日が日曜日  
  1月1日が「木」曜日なら...   2月1日が日曜日   3月1日が日曜日  11月1日が日曜日  
  1月1日が「金」曜日なら...   8月1日が日曜日  
  1月1日が「土」曜日なら...   5月1日が日曜日  
  1月1日が「日」曜日なら...   1月1日が日曜日  10月1日が日曜日  

閏年の場合
  1月1日が「月」曜日なら...   9月1日が日曜日  12月1日が日曜日  
  1月1日が「火」曜日なら...   6月1日が日曜日  
  1月1日が「水」曜日なら...   3月1日が日曜日  11月1日が日曜日  
  1月1日が「木」曜日なら...   2月1日が日曜日   8月1日が日曜日  
  1月1日が「金」曜日なら...   5月1日が日曜日  
  1月1日が「土」曜日なら...  10月1日が日曜日  
  1月1日が「日」曜日なら...   1月1日が日曜日   4月1日が日曜日   7月1日が日曜日  

したがって,閏年か否かにかかわらず
1月1日が日〜土のいかなる曜日で始まっても
その年には必ず1回以上,月初めの1日(ついたち)が日曜日となる月があり,
その月の13日が金曜日となる。

問題 5

13日の金曜日が1年に3回あるのはどんな条件の年か。

解答:

閏年ではなくて,1月1日が「木曜日」の場合,2月13日,3月13日,11月13日の3回。

または閏年であって,1月1日が「日曜日」の場合,1月13日,4月13日,7月13日の3回。

ついでに言えば,13日の金曜日は1年に4回以上はないこともわかる。