2012年11月5日月曜日

ツェラーの公式:曜日を計算する

年月日が与えられたときに、その曜日を計算する公式として「ツェラーの公式 (Zeller's Congruence)」がある。

Wikipedia によると、
ツェラーの公式(Zeller's congruence)とは西暦の年、月、日からその日が何曜日であるかを算出する公式である。 まず求めたい日の年の千の位と百の位の連続の数字(例えば2310年ならば23)をJ、年の下2桁(例えば2310年ならば10)をK、月をm、日をq、曜日をhとする。ただし求めたい日の月が1月、2月の場合はそれぞれ前年の13月、14月とする(例えば、2007年1月1日なら2006年13月1日とする)。


hが0なら土曜日、1なら日曜日、2なら月曜日、3なら火曜日、4なら水曜日、5なら木曜日、6なら金曜日である。
とのことらしい。
はガウスの記号で、x を超えない最大の整数(小数点以下切り捨て)を表す。
この記事では、以下 [ x ] で表すことにする。

なお、上の式は現在使われているグレゴリオ暦の場合であって、ユリウス暦の場合には少し違う式になる。詳細は Wikipedia 参照。

計算してみよう。2012年8月26日 日曜日の場合。

J=20, K=12, m=8, q=26。
26 + [ (8+1)*26 / 10 ] + 12 + [12/4] + [20/4] -2*20
= 26 + [234/10] + 12 + [3] + [5] - 40
= 26 + 23 + 12 + 3 + 5 - 40
= 29

29 mod 7 = 1 なので確かに日曜日だ。

確かにこの式が正しいのか調べてみよう。
ある年月日が正しいと仮定して、次の日も正しいことを確認する。


次の日が月が変わらない場合


式の形から、mod 7 する前の値が1増えるだけなので自明。


「 (m+1) * 26 / 10 」の意味


月が変わる場合を調べる前に、月の値が出現する 「 (m+1) * 26 / 10 」 という謎の式の意味を調べてみよう。 各月ごとの (m+1) * 26 、および、[(m+1) * 26] を調べる。

m値(m+1)*26[(m+1)*26/10]次月との差
113364363
21439039-29
33104103
44130132
55156153
66182182
77208203
88234233
99260262
1010286283
1111312312
1212338333

黄色い行は大の月。
この表を見ると、末日が31日の大の月では次月との差が3、末日が30日の小の月では次月との差が2になっていることがわかる。素晴らしい。
+1して26を掛けるというのはちょうどこのようになるための工夫だったのだろう。


月(m)が変わるが年(J、K)が変わらない場合(=2月末日以外の月末の場合)


mの定義(1月、2月は前年の13月、14月として扱う)により、2月末日以外ではJ、Kの値は変わらず、mが+1されるだけになる。

大の月の翌日では、日(q)は31→1で-30。 [ (m+1) * 26 ] が前節の結果より+3。差引-27であり、mod 7 値は1になるので次の日は曜日がひとつ進むことがわかる。

2月以外の小の月も同様で、qが30→1で-29。[ (m+1) * 26 ] が+2なので、大の月と同じく差引-27で次の日の曜日がひとつ進んでいることがわかる。
つまり、

2月以外の末日の翌日は、各項の合計値は -27 小さくなり、
-27 mod 7 = 1 で曜日がひとつ進む。

ということになる。


2月末日の翌日の場合


2月の以外では正しいことが分かったので問題の2月。
式の定義により2月末日までは年は前年扱いなので、その翌日になるところでJ、Kの値が変化する。
閏年か否かによって日(q)値が29→1、28→1と変わるので、その違いをJ、Kの値の更新によって吸収するというのがJ、Kが出現する項の趣旨になる。

グレゴリオ暦での閏年は、
  1. 年が400で割り切れれば閏年。
  2. 年が400で割り切れず、100で割り切れたら閏年。
  3. 上記以外で4で割り切れたら閏年。
  4. 年が4で割り切れなければ閏年ではない。
となっている。

[J/4] - 2J
Jは西暦の上2ケタ。
この式の意味は、「100年ごとに2減るが、400年目では1しか減らない」という式になる。
閏年ルールの1番目と2番目を表している。

K + [K/4]
Jは西暦の下2ケタ。
この式は毎年1増えるが、4で割り切れる年には2増えるという式になる。
ただし、99 → 0 になるとき(100で割り切れた年)は 123 → 0 なので -123 になる。

まとめてみよう。

閏年?[J/4] - 2JK + [K/4] 差引きmod 7
400 で割り切れる年-1-123-124+2
100 で割り切れる年(上以外)×-2-123-125+1
4 で割り切れる年(上以外)0+2+2+2
4 で割り切れない年×0+1+1+1

閏年の場合は2つ、そうでない年は1つ曜日を進める効果がある。
他の項と合わせてみよう。

閏年?K + [K/4]
+ [J/4] - 2J
[(m+1) * 26 / 10] q 合計mod 7
400 で割り切れる年-124-29-28-1811
100 で割り切れる年×-125-29-27-1811
4 で割り切れる年+2-29-28-551
4 で割り切れない年×+1-29-27-551

どの年でも2月末日から3月1日で曜日がひとつ進んでいることがわかる。
各項ごとに mod 7 値で見たほうが各項の効果がわかりやすいかもしれない。

閏年?(K + [K/4]
+ [J/4] - 2J)
mod 7
([(m+1) * 26 / 10])
mod 7
q mod 7合計
mod 7
400 で割り切れる年+2+601
100 で割り切れる年×+1+6+11
4 で割り切れる年+2+601
4 で割り切れない年×+1+6+11

閏日の有無による Δq の変動をを K と J の項で絶妙に打ち消していることがわかる。

以上より、
2月の末日の翌日は、閏年の有無によらず 
mod 7 値は1増加(または6→0)して曜日がひとつ進む。




以上の結果から、 Zeller の公式で「ある日の曜日が正しければ次の日の曜日も常に正しい」ことが分かった。グレゴリオ暦の最初の日、1582年10月15日(金曜日)は公式でも正しく金曜日になるのでそれ以降もずっと正しいことがわかる。

また、同様なやり方で「ある日の曜日が正しければ前の日の曜日も常に正しい」ことも容易に示せるはず。

この式の素晴らしいところは、
  • 不規則に出現する大小の月による月末処理を「 (m+1) * 26 / 10 」という式で見事に吸収していること。
  • 2月までを前年として扱うことで、閏年による2月末日の変化を年切り替えに対応させて吸収していること。
だと思う。


(おまけ)負数の mod


計算中に負数の mod 7 が出てくることがあるが、普通は正値になるまで7の倍数を足して mod 7を考える。 ところが、-6倍してから mod 7 しても正しい結果になる。

「 x - ( -6x) = 7x 」なので確かに mod 7 値は不変になるのだがそんな計算方法があるとは知らなかった。

一般化すると「負数の x に対する x mod k は -(k-1) x mod k で計算できる」となる。



0 件のコメント:

コメントを投稿