プチコンで遊ぼう! (はてなブログ版)

任天堂3DSのプチコンで遊ぼう! [twitter:@eida_s]

はてなダイアリーから移行しました。 はてなダイアリーのURLを開いても自動的にこちらにリダイレクトされますのでご了承ください。

140曜日計算とツェラーの公式

先日、ツェラー(zeller)の公式を使った140曜日計算をツイートしましたが、リスト写しまちがえで何度もツイートしなおしたので、最終版を掲載するのと、ツェラーの公式についての説明を載せておきます。

■140曜日計算

何度も修正ツイートしたのですが、最後にツイートしたものもM=M+(M<3)のところが(N<3)とかなってて間違ってます。すみません。
最終版は以下のとおりです。
これはPTCファイルから直接コピペしたので間違ってないはずです。

DTREAD(DATE$),Y,M,D:Y=Y-(M<3)M=M+(M<3)*12↓
H=(Y+(0OR Y/4)-(0OR Y/100)+(0OR Y/400)+FLOOR((13*M+8)/5)+D)%7?MID$("SUNMONTUEWEDTHUFRISAT",H*3,3)


ツェラーの公式

ツェラー(zeller)の公式とは、任意の日付の曜日を計算するための公式です。
パソコン黎明期から、カレンダープログラムなどでよく使われておりました。
自分も昔プログラムを作った時は、なぜそのような式になるのか、まったく理解しないまま使った覚えがあります。

今回は、ちゃんと理解してからソース書きましたので、覚書という意味と、基本的な導入のところでつまづく方向けに少し解説を書いておきます。


(1)基本的な考え方

ある基準日を定めて、この日を例えば日曜日とします。
この基準日からk日後の曜日はなんでしょうか。

h=k mod 7とします。「k mod 7」とは、「kの7による剰余」を表します。つまり、「整数kを7で割った時の余り」のことです。
このhが0の時はもとの曜日と同じであることを表します。

例えば、k=7だったら、7を7で割った余りは0なので、同じ日曜日であることがわかります。
つまり、7,14,21,28,...と7の倍数日後は全て日曜日になることがわかります。

また、余りが1から6であれば、日曜日の1から6日後なので、余りが1,2,3,4,5,6であれば、それぞれ月曜日、火曜日、水曜日、木曜日、金曜日、土曜日であることがわかります。

このように、基準日の曜日がわかっていて(条件1)、かつ曜日を求めたい日が基準日からk日後であることがわかれば(条件2)、求めたい日の曜日を求めることができます。

例えば2013年4月1日を基準日とします。この日は月曜日だった(条件1を満たす)。
2013年4月25日は何曜日でしょうか。
4月25日は4月1日の24日後です(条件2を満たす)。
24を7で割った余りは3です。
基準日とした4月1日は月曜日でした。その3日先の曜日だから、4月25日は木曜日です。カレンダーで確かめるとその通りであることがわかります。

では、5月1日は何曜日でしょうか。


(2)任意の年月日の曜日を求めるための基本的な考え方

5月1日は何曜日でしょうか。

5月1日は4月1日の一月先です。
4月は30日まであります。
ということは、5月1日は4月1日の30日後です。
k=30だから、30 mod 7は2です。
4月1日は月曜日だったからその2日先の曜日は水曜日です。

2014年4月1日は何曜日でしょうか。
これも同じ考え方で計算できます。
2014年4月1日は2013年4月1日の365日後です。
これでk=365 mod 7=1で、火曜日とわかりました。

では仮に1年1月1日の曜日がわかっているとします。
すると、任意の年月日y年m月d日の曜日は、1年1月1日からy年m月d日までの経過日数を7で割ればわかることになります。

原理的には、この方法で任意の日付の曜日を求めることができます。
しかし、実際には上記の経過日数を求めることは簡単にはすみません。

考慮しなければならないルールが2つあります。

ひとつ目のルールは、ひと月の日数が一定でないのでそれを考慮すること。
1〜12月のそれぞれの日数は、31,28(閏年は29),31,30,31,30,31,31,30,31,30,31です。

ふたつ目のルールは、閏年の考慮です。
閏年に関するルールとしての暦には、ユリウス暦グレゴリオ暦があるが、現在の日本ではグレゴリオ暦が採用されているので、以後はグレゴリオ暦についてのみ考えます。
グレゴリオ暦では、閏年は以下のルールで決まっています。
その年(yとしよう)が4で割りきれる時、閏年です。ただし、100で割りきれる時は閏年でない。ただし、400で割りきれる時は閏年となります。
このややこしいルールは、400年の間に97回の閏年を入れるためのもので、こうすることで400年間の平均の一年の日数を365.2425日にして、実際の地球の公転周期にできるだけ近づけようとする工夫なのです。

上記二つのややこしいルールを、うまいこと一つの式で表したのが、ツェラーの公式です。


(3)フェアフィールドの公式へ

さて、wikipediaの「ツェラーの公式」の項を見ると、フェアフィールドの公式(公式Ⅰ)から、公式Ⅱに変形し、さらに最終的なツェラーの公式(公式Ⅲ)に変形しています。

このフェアフィールドの公式(公式Ⅰ)とはどういうものでしょうか。

この式は、グレゴリオ暦が1年1月1日から施行されているとして、1年1月1日からの経過日数を計算し、経過日数の7の剰余を求めることで曜日を求める式となっています。
この一つの式の中に、複雑な月と閏年の計算を含んだものとなっています。
mod 7の部分を除くと、1年1月1日からの経過日数だけを知ることもできます。
例えば、2013年4月1日をこの式にあてはめると、734958日(後述の注意書き参照)であることがわかります。

実際にはグレゴリオ暦が施行されたのは1582年10月15日ですが、仮に1年1月1日から施行されていたとすると、1年1月1日は月曜日だとわかっています。
すると、734958の7の剰余を求めると0であるので、2013年4月1日は月曜日であることがわかります。

なお、少し補足の必要がありますが、上記のページの式で見ると、「y 年 m 月1日 〜 y 年 m 月 d 日」が「d (日)」となっています。
m月1日〜m月d日ならば本来は、 d-1日間なのだが、ここではd日間となっています。
これは式の間違いではなく、7の剰余をとった時の 0に相当する曜日を日曜日にあわせるため、式全体に1を足しているためです。
つまり、公式Ⅰからmod 7を除いた式、 365y+[y/4]-[y/100]+[y/400]+[306*(m+1)/10]+d-428 ([ ]はFLOOR関数を表す)で算出されるのは、1年1月1日も含んだ(いうなれば基準日1年1月0日からの)経過日数になっているので、厳密に1年1月1日からの経過日数としたい場合には式からさらに1を引いて、最後の項を-429とする必要があります。

さて、上記の式で実際に曜日はわかりますが、これは結構大きな数の計算をしなくてはならず、暗算などで曜日を知りたいときには不便です。
また、コンピュータで扱う場合でも、16ビットの整数演算を超えてしまうので、黎明期の8ビットパソコンで計算するにも不向きでした。

そこで、フェアフィールドの公式を、剰余計算の法則を使って変形し、暗算あるいは筆算で簡単に計算できるようにしたのがツェラーの公式です。
(ツェラーの公式が生まれたのは19世紀なので、もちろん、8ビットコンピュータのために作った公式ではありません。)

人が手計算をするには公式Ⅲが適しますが、コンピュータが計算するには16ビットの演算範囲さえ超えなければよいので、手順が簡単な公式Ⅱを用いることが多いです。
140曜日計算で使った式もこの公式Ⅱです。

もう少し補足が必要そうですが、かなり長文となったので、とりあえずここまで。