web-dev-qa-db-ja.com

曜日を見つけるための坂本アルゴリズムの正確性

坂本のアルゴリズムを使用して、特定の日付から曜日を見つけています。誰かがこのアルゴリズムの正確さを教えてもらえますか?私は2000年から2099年までこれが欲しいだけです。

Wikipedia のアルゴリズムが参照用に提供されています。

int dow(int y, int m, int d)
{
   static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
   y -= m < 3;
   return (y + y/4 - y/100 + y/400 + t[m-1] + d) % 7;
}
57
Harikrishnan

t[]配列が正しいと仮定すると、12個のスポットチェックで確認できます(月ごとに任意の日/年を使用して確認できます)。

y -= m < 3は素晴らしいトリックです。 3月1日に始まり、2月28日(または29日)に終了する「仮想年」を作成し、その年のendに余分な日(ある場合)を入れます。というか、前年年の終わりに。たとえば、2011年の仮想年は3月1日に始まり、2月29日に終了し、2012年の仮想年は3月1日に始まり、次の2月28に終了します。

うるう年の追加日を仮想年の終わりに置くことにより、残りの表現が大幅に簡素化されます。

合計を見てみましょう:

(y + y/4 - y/100 + y/400 + t[m-1] + d) % 7

通常の年には365日あります。 52週間と1日です。したがって、一般的に、曜日は1年に1日シフトします。それがy用語の貢献です。年ごとに1日に1が加算されます。

しかし、4年ごとはle年です。それらは4年ごとに1日余分に貢献します。仮想年を使用することで、合計にy/4を追加して、y年でうるう日がいくつあるかをカウントできます。 (この式は整数除算の丸めdownを想定していることに注意してください。)

しかし、100年ごとはうるう年ではないため、それはまったく正しくありません。したがって、y/100を減算する必要があります。

ただし、400年ごとが再びうるう年であることを除きます。したがって、y/400を追加する必要があります。

最後に、月の日dと月に依存するテーブルからのオフセットを追加します(年内の月の境界はかなりarbitrary意的であるため)。

それから週7の長さなので、mod 7を全部使いましょう。

(たとえば、週が8日間の場合、この式で何が変わるのでしょうか?さて、mod 8です。当然、y5*yである必要があります。なぜなら、365%8 = = 5.また、月のテーブルt[]を調整する必要があります。それだけです。)

ちなみに、カレンダーが「9999までは良い」というウィキペディアの声明は完全にarbitrary意的です。この式は、10年、100年、1000年、100万年のいずれであっても、 グレゴリオ暦 に固執している限り有効です。

[編集]

上記の議論は本質的に帰納法による証明です。つまり、特定の(y、m、d)に対して式が機能すると仮定すると、あなたはそれが機能することを証明する y + 1、m、d)および(y、m、d + 1)。 (yは3月1日から始まる「仮想年」です)ですから、重要な質問は、ある年から次の年に移動すると、合計額は正しい量だけ変化するのでしょうか?うるう年の規則を知っていて、年末に余分な日がある「仮想年」では、ささいなことをします。

119
Nemo

最近、私はこのアルゴリズムについてのブログ投稿を書きました here

アルゴリズムの背後にある基本的な考え方は、2月と1月に前の年の12月31日から曜日をカウントすることです。他のすべての月については、current year 31 Decから曜日をカウントします。最初に2つのステップでこれを行い、現在の月の前の月の最終日の曜日を計算しますm 7を法としてdを追加します。

31 Dec 1 BCは0としてエンコードされる日曜日であり、月曜日は1などです。したがって、0 + y + y/4 - y/100 + y/400 this with y -= m < 3は、今年の12月31日または前年(月に応じて)の曜日を計算します)。注:365 % 7 == 1は、365*yではなくyを記述した理由を説明しています。最後のコンポーネントdは、前月の前日から曜日のカウントを開始するため明らかです。

説明する必要がある最後の部分は配列の値です。最初の2つの値は、昨年12月31日から月の開始までの日数% 7です。残りの月については、7を法とする負の値前の月の終わりから現在の年の12月31日までの日数です。言い換えれば、7を法とする加算によって日数を引いています。 (a-b)%7 = (a+(7-b%7))%7

私のブログ投稿であなたが見つけるかもしれないより多くの説明。

3
csharpfolk