グレゴリオ暦と閏年

 日暦算 の難問です。問題を解きながら、普段私たちが使っている暦の仕組みについて学びましょう。

【算数問題01】閏年は400年間に何回ある?

 天文観測によって正確に測った 1 年の長さを太陽年といいます。その長さは

 1 太陽年 = 365.2422 日

であることが知られています。現在、私たちの使っているカレンダーは グレゴリオ暦 といって、次の規則によって閏(うるう)年を入れて 1 年の長さをできるだけ太陽年に近くなるように調整しています。閏年 というのは、他の年の 365 日に比べて日数が 1 日だけ多い年のことです。

 ① 4 で割り切れる年は閏年とします。
 ② ただし、100 で割り切れる年は平年とします。
 ③ ただし、400 で割り切れる年は閏年とします。

 ③が②より、②が①よりも優先します。たとえば西暦 2000 年は 4, 100, 400 のいずれでも割り切れますが、③の条件を優先的に適用して閏年となります。以上の説明を踏まえて解答してください。

(1) 西暦 1 年から西暦 400 年の間に閏年は何回ありましたか。
(2) グレゴリオ暦の 1 年の長さは平均すると何日でしょう。
(3) グレゴリオ暦における平均の 1 年の長さと 1 太陽年の誤差は何秒ですか。

【ヒント】(3) は「秒数」で答えることに注意してください。

【解答】(1) 400 のうち 4 で割り切れる数は 100 個あります。規則①によって、とりあえずこれらの数をいったん閏年にしておきます。ところが②によると 100 で割り切れる年、すなわち 100, 200, 300, 400 の 4 つの年(いずれも 4 で割り切れます)については、「やっぱり閏年はやめて平年にしておこう」となるので、100 個からこの 4 個を取り除いて 96 個になります。最後に③の条件を適用します。「それでもやっぱり 400 だけは閏年にしようかな」ということで、もう1度閏年のグループに戻して 97 回となります。

(2) グレゴリオ暦では平年には 365 日、閏年には 366 日あるのですが、これをならして平均を求めてみようという問題です。 (1) の結果により、97 年間は 366 日、303 年間は 365 日です。400 年間の全日数は
 
\[303\times 365+97\times 366=110595+35502=146097\ 日\]
となります。これを 400 で割ると 1 年当たりの平均日数が求められます。
 
\[146097\div 400=365.2425\ 日\]
 これが答えなのですが、実はもう少し簡単な計算法があります。それは 365 日からの「ずれ」だけを取り集めて、それを 400 等分して各年の日数(365 日)に配ってしまおうという考え方です。つまり 400 年で閏年のずれを全部集めると 97 日ありますから、それを 400 で割ると、
 
\[97\div 400=0.2425\ 日\]
となります。これを平年の 365 日に配れば、
 
\[365\div 0.2425=365.2425\ 日\]
という答えが得られます。

(3) 1 太陽年は 365.2422 日ですから、これをグレゴリオ暦の 1 年 365.2425 日から引き算して、
 
\[365.2425-365.2422=0.0003\ 日\]
となります。これは日数ですから感覚的によくわかりませんね。秒数に変換してみましょう。1 日は 24 時間、 1 時間は 60 分、 1 分は 60 秒ですから、これを次々と掛け合わせると、
 
\[0.0003\times 24\times 60\times 60=25.92\ 秒\]
という答えが得られます。

【グレゴリオ暦に代わる暦のアイデアを募集します】
 1 年で約 26 秒の誤差ですから、グレゴリオ暦はとても優秀ですね。でも「もっと良い方法がないかな?」と色々考えてみることも大切です。私もあれやこれやと考えてみているのですけど、なかなか難しいですね。考え過ぎて頭が疲れてしまったので、皆さんから「新しい暦の案」を募集することにしました。いいアイデアがあったら、下のほうにあるコメント欄からお寄せください。

【VBA】うるう年判定関数

 VBA でうるう年を判定する (うるう年なら True, 平年なら False を返す) 関数を作成してみましょう。うるう年であることの条件をそのまま素朴に実装するなら、次のようなコードになるでしょう。

'[VBA] うるう年判定関数

Function LEAP(year As Long) As Boolean

  Dim x As Boolean

  x = False

  '4で割り切れたら判定を真にする
  If year Mod 4 = 0 Then
    x = True
  End If

  '100で割り切れたら判定を偽にする
  If year Mod 100 = 0 Then
    x = False
  End If

  '400で割り切れたら判定を真にする
  If year Mod 400 = 0 Then
    x = True
  End If

  LEAP = x

End Function

 ブール型変数 x の初期値を False とし、条件を満たす毎に値を変更していきます。シンプルでわかりやすいですが、やや冗長なコードですね。もう少し洗練させたいところです。うるう年であるための条件を再掲します。

 ① 4 で割り切れる年はうるう年とします。
 ② ただし、100 で割り切れる年は平年とします。
 ③ ただし、400 で割り切れる年はうるう年とします。

 条件 ② と ③ を先にまとめて処理することを考えます。つまり、100 で割り切れて 400 で割り切れない年は、(条件の優先順位が高いので) 平年と決まります。この条件に当てはまらない年のうち、さらに条件 ① を満たす年だけがうるう年となります。ElseIf 構文をうまく使って実装してみましょう。

'[VBA]うるう年判定関数

Function ISLEAP(year As Long) As Boolean

  Dim x As Boolean

  If year Mod 100 = 0 And year Mod 400 <> 0 Then
    ISLEAP = False
  ElseIf year Mod 4 = 0 Then
    ISLEAP = True
  End If

End Function

 かなり短いコードになりましたね。この関数を使って、西暦 2000 年から 2200 年までのうるう年を並べてみましょう。

'[VBA]うるう年の一覧

Sub LeapYearList()

  Dim i As Long

  For i = 2000 To 2200
    If LEAP(i) = True Then
      Debug.Print i;
    End If
  Next i

End Sub

'実行結果
2000  2004  2008  2012  2016  2020  2024  2028  2032  2036
2040  2044  2048  2052  2056  2060  2064  2068  2072  2076
2080  2084  2088  2092  2096  2104  2108  2112  2116  2120
2124  2128  2132  2136  2140  2144  2148  2152  2156  2160
2164  2168  2172  2176  2180  2184  2188  2192  2196

 実行結果をチェックしてみましょう。2000 年は 400 で割り切れるのでうるう年ですね。2100 年や 2200 年は 400 で割り切れず、100 で割り切れるので平年となっています。

コメント