萬年曆程式
一、幾月幾日星期幾
在探討萬年曆程式之前,我們先來看一下如果得知今年幾月幾日是星期幾,假設某月的 1 日是星期二,那麼 2 日就是星期三,而 N 日則是 (N-1+2) 再取七的餘數即可得知它是星期幾(星期天為 0)。我們再把這個公式再化簡,如果我們記錄該月第 1 日前一天的星期為 W,則該月第 N 日的星期則為 (N+W)%7 。如果我們可以先求出每個月的這個數字 W,將它放在陣列中,即可快速地求出每個月的任何一天是星期幾。以 2003 年為例,這一年十二個月的 W 值分別為 2、5、5、1、3、6、1、4、0、2、5、0,而這段程式可以寫成:
#include <stdio.h>
void main()
{
int W[12]={2, 5, 5, 1, 3, 6, 1, 4, 0, 2, 5, 0};
int m, d;
scanf("%d %d",&m, &d);
printf("%d\n", (W[m-1]+d)%7);
}
二、萬年曆程式
接下來我們希望這個程式可以輸入西元紀年、月份、日期,然後輸出它是星期幾。我們先列出 2002 到 2004 年每個月的 W 值:
|
年份 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
|
2002 |
1 |
4 |
4 |
0 |
2 |
5 |
0 |
3 |
6 |
1 |
4 |
6 |
|
2003 |
2 |
5 |
5 |
1 |
3 |
6 |
1 |
4 |
0 |
2 |
5 |
0 |
|
2004 |
3 |
6 |
0 |
3 |
5 |
1 |
3 |
6 |
2 |
4 |
0 |
2 |
從上表我們可以發現 2003 每個月的 W 值都是 2002 年的 W 值加上一,這是因為一年有 365 天, 365 除以 7 之後餘數為 1,因此每年同一天的星期會比上一年多 1。再看看 2004 年的部分,一、二月的還是 2003 年的加 1,但是三月以後的則是比 2003 年的多 2。這是因為 2004 是閏年,二月多了一天的緣故。從這邊我們可以得知,如果我們知道某一年的 W 值,即可以用年份及閏年的年數去求出當年的 W 值。閏年的規則為:
在這裡我們希望知道西元 0 年的 W 值,我們先用 2003 年的 W 值來計算,之後再用差值即可推出西元 0 年的 W 值。下面的程式我們用小寫的 w 代表該月份的 W 值,而大寫的 W 陣列則是西元 0 年的 W 值,程式如下:
#include <stdio.h>
void main()
{
int W[12]={2, 5, 5, 1, 3, 6, 1, 4, 0, 2, 5, 0};
int y, m, d, w;
scanf("%d %d %d",&y, &m, &d);
w=W[m-1]+y+(y/4)-(y/100)+(y/400);
if( ((y%4)==0) && (m<3) ) {
w--;
if((y%100)==0) w++;
if((y%400)==0) w--;
}
printf("%d\n", (w+d)%7);
}
上面黃色的那一段程式為判斷是否為閏年,如果是的話,三月以後的 W 值要加上 1。這段程式執行後輸入 2003 12 18,結果為 0,比真正的少 4,所以我們要改 W 值加上 4 才是正確的:
#include <stdio.h>
void main()
{
int W[12]={6, 2, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
int y, m, d, w;
scanf("%d %d %d",&y, &m, &d);
w=W[m-1]+y+(y/4)-(y/100)+(y/400);
if( ((y%4)==0) && (m<3) ) {
w--;
if((y%100)==0) w++;
if((y%400)==0) w--;
}
printf("%d\n", (w+d)%7);
}
另外,因為我們用的西元紀年在 1582 年曾經改曆過,所以我們輸入的年份要在 1583 以後才有用,否則算出來的答案是錯的。下面我們列出幾個閏年及非閏年的日期,以印證我們的程式:
1911 10 10 -> 2
1892 3 3 -> 4
2000 1 1 -> 6
1900 6 18 -> 1