閏年判定
閏年は「何年に何回」訪れるでしょう?
「4年に1回」と思っている⽅が⼤半だと思います。私達の⼤半が⽣きている間は「4年に1回」と思っていても困ることはありません。でも正確には「4年に1回」ではありません。
そもそも閏年は何のためにあるのかは皆さんご存知と思いますが、念のためおさらいしておきましょう。地球は365⽇かけて太陽の周りを⼀周していて、それが1年に相当するのですが、正確には365⽇より6時間ほど長く、それが4年分蓄積すると24時間=1⽇分だけ⼀周に届かなくなるので、4年に⼀度1年の⽇数を1⽇増やすことで⾜りない分を補うために閏年が設けられています。
だったら「4年に1回」が正解で良いじゃないかと思われるかもしれませんが、それほど単純な話ではなく「365⽇より6時間ほど長く」の「ほど」の部分を無視する訳にはいかないので細かい調整が必要になります。「6時間ほど」の部分をもう少し正確に秒単位まで計測した値は「5時間49分12秒」となります(どうやって測ったのでしょう)。つまり6時間より10分48秒短いのです。1年にたった10分48秒ですが、閏年が10回あれば1時間48分の誤差となります。これを放っておくわけには⾏きません。「4年に1回」では閏年はちょっとだけ多いのです。
この10分48秒の誤差を解消するためにちょっと多い閏年を間引いてやる必要があります。10分48秒=648秒を1⽇の秒数 =86400秒で割ってやると0.0075となります。つまり「4年に1回」では閏年は0.0075⽇だけ多いことになります。0.0075 = 75/10000 = 3/400なので0.0075⽇を分数で表すと3/400⽇となります。これにより、閏年は「400年に3回だけ余計」であるということが分かります。「4年に1回」のペースだと400年だと閏年は100回あることになりますが、その余計な3回を間引いた
「400年に97回」
がより正確な閏年の回数となります。もっと厳密なことを⾔うと⼀年の⻑さは不変ではないし、秒以下の誤差もあるので数千年後には誤差が⽣じるでしょうが、現時点ではこれがもっとも正確な閏年の回数です。
具体的には現⾏のグレゴリオ暦では「4で割り切れる年、ただし『100で割り切れて400で割り切れない年』は除く」が閏年となります。つまり下2桁が00ではない年は普通に「4で割り切れる年は閏年」と思っておいて⼗分なのですが、下2桁が00の年(=100で割り切れる年・当然4でも割り切れる)の場合は400で割り切れるかどうかで閏年であるかどうか違ってきます。
直近では2000年が下2桁が00の年でしたが、2000は400でも割り切れる年のため通常通り閏年となりました。しかしその前の下2桁が00の年である1900年は「100で割り切れて400で割り切れない年」のため4で割り切れる年なのに閏年ではありませんでした。
次に来る下2桁が00の年である2100年も400で割り切れないので同様に閏年ではありません。⾔い換えると「4で割り切れるのに閏年ではない」という年は2100年までやってきません。冒頭で「私達の⼤半が⽣きている間は」と書いたのはそのためです。2100年ごろに「今年って閏年じゃないの?」と騒ぐ様⼦が⾒たかったのに残念です。どうしてもそれを体験したい⽅は頑張って2100年まで⽣きてください。
ソフトウェア開発の会社らしくプログラムに関する話もしておきましょう。⻄暦年 y が「4で割り切れる年、ただし『100で割り切れて400で割り切れない年』は除く」に該当するかどうかを判定する処理(閏年の判定)をどう書くか?という問題はなかなか興味深いところではあります。⼀例としてかぎ括弧内の⽂をそのままコードにすると次のように書くことができます。
fun isLeapYear(int: y): Boolean {
return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0)
}
% は割り算の余りを求めるために⽤いる剰余演算⼦というとても重要な演算⼦で、y % 4 は y を4で割った余りとなります。
もっとトリッキーな書き⽅もできますが、そうなると元のアルゴリズムがわかりにくくなるため個⼈的にはこのくらいの野暮なコードがちょうど良いと思っています。
閏年については他にもいくつかトピックがあって、たとえば「各年の1⽉1⽇の曜⽇を求める」という問題はなかなか⾯⽩いのですがそれはまた別の機会に。⻑⽂失礼しました。
参考:⿊後家蜘蛛の会(アイザック・アシモフ)4巻第5話
(担当:六度七分)