基姆拉爾森公式 (kim larsen calculation formula) 用於給定年份 \(y\) , 月份 \(m\) 和日子 \(d\) 的條件下,計算該天是星期幾。
初始條件:從公元 0 年 1 月 1 日,星期日開始計算(ps:公元 0 年不是閏年)。
輸入: \(y, m, d\) 三個整數表示年月日。
輸出:\(w \in [0, 6]\) 分別表示星期日到星期六。
下面為推導過程。
對於公元 0 年的第乙個月:
\[w = (d-1) \mod 7
\]考慮公元 0 年後的年份,不考慮閏年(即假設每年均為 365 天),因為365 % 7 == 1
,所以公元 1 年第一天是星期一,公元 2 年第二天是星期二,以此類推。在不考慮閏年的情況下,對於公元 \(y\) 年的 1 月:
\[w = (d-1+y) \mod 7
\]考慮閏年,在 \([0,y-1]\) 區間內假設有 \(k\) 個閏年,那麼公元 \(y\) 年第乙個月的 \(w\) 需要加上 \(k\) 進行修正。
閏年的條件是:整除 400 或者整除 100 但不能整除 4 。所以 \(k = (y-1)/400 + ((y-1)/4 - (y-1)/100)\) ,其中/
表示 c 語言的整型除法。
所以,在考慮閏年的情況下,公元 \(y\) 年的1 月的星期幾:
\[w = [d-1 + y + (y-1)/400 + ((y-1)/4 - (y-1)/100)] \mod 7
\]接下來,將上述公式推廣到公元 \(y\) 年的任意月份。
公元 \(y\) 年的第 29 天的 \(w\) 值總是與第一天相同的。以 28 為基準,某些月份會多出幾天的偏移,比如日期0/1/1
是星期日,w = 0
,1 月的偏移為 3 ,所以 日期0/2/1
是星期三,w = 3
。
現在我們需要找出每個月份基於 1 月的偏移量(暫不考慮閏年)。
月份累計偏移
該月偏移
累計偏移模 710
3023
0333
3346
2658
31611
24713
36816
32919
25102130
112423
122635
以陣列記錄**的第 4 列:disp =
.
所以,不考慮考慮閏年的情況下,對於任意的 \(m\) 有:
\[w = \ \mod 7
\]如果考慮閏年,那麼 2 月之後的月份的 \(w\) 值都需要加 1 處理。
**表示即為:
#define isleap(y) (((y) % 400 == 0) || (((y) % 4 == 0) && ((y) % 100 != 0)))
int week(int y, int m, int d)
; int w = ((d - 1) + y + ((y - 1) / 400 + (y - 1) / 4 - (y - 1) / 100) + disp[m]) % 7;
if (m > 2 && y != 0 && isleap(y))
w = (w + 1) % 7;
return w;
}
下面考慮如何優化,因為disp
陣列每次使用都要手動算一遍。當然,下面很多技巧都是根據已有的結果反推過程而已。
假設把n
年的 1 月和 2 月「劃分」到n-1
年,作為n-1
年的第 13 月和 14 月。而每年的第一天是 3 月 1 日,即每年的月份範圍是 \([3, 14]\) .
公元 0 年 3 月 1 日是星期三,所以對於公元 0 年 3 月有:
\[w = (d+2) \mod 7
\]考慮閏年,對於公元 \(y\) 年的 3 月,這時候需要考慮的是 \([0,y]\) 範圍內的閏年個數,/
表示 c 語言整型除法:
\[w = [d+2+y+(y/400+y/4-y/100)] \mod 7
\]偏移量**:
月份累計偏移
該月偏移
累計偏移模730
3043
2355
3568
21710
33813
36916
22101834
112120
122332
132635
142901
暫時引入disp[m] (3 <= m <= 14)
記錄累計偏移模 7 .
考慮閏年,推廣到任意月份 \(m\) :
\[w = \ \mod 7 \quad (3 \le m \le 14)
\]這時候 2 月是該年的最後乙個月,不需要考慮上述「如果考慮閏年,那麼 2 月之後的月份的 \(w\) 值都需要加 1 處理」的情況。
站在巨人的肩膀上,可以發現(/
表示整型除法):
\[disp[m] = (2m+3(m+1)/5-1) \mod 7, \quad (3 \le m \le 14)
\]所以:
\[w = [d + y + 1 + (2m+3(m+1)/5) + (y/400+y/4-y/100)] \mod 7 \quad (3 \le m \le 14)
\]
int calcdayofweek(int y, int m, int d)
完整帶測試**:
#include #include #include #include #define isleap(y) (((y) % 400 == 0) || (((y) % 4 == 0) && ((y) % 100 != 0)))
#define yearlim (10000)
using namespace std;
int week(int y, int m, int d)
; int w = ((d - 1) + y + ((y - 1) / 400 + (y - 1) / 4 - (y - 1) / 100) + disp[m]) % 7;
if (m > 2 && y != 0 && isleap(y))
w = (w + 1) % 7;
return w;
}int calcdayofweek(int y, int m, int d)
const char *format = "%4d/%02d/%02d\n";
void test(int y, int m, int d)
int main()
cout << "-------------------" << endl;
// leap years test
for (int i = 0; i < 1000; i++)
// feb. test
cout << "-------------------" << endl;
for (int i = 0; i < 1000; i++)
// 29,feb test
cout << "-------------------" << endl;
for (int i = 0; i < 1000; i++)
}
基姆拉爾森計算公式
功能描述 根據輸入的日期判斷當天是週幾 演算法如下 基姆拉爾森計算公式 w d 2 m 3 m 1 5 y y 4 y 100 y 400 mod 7 在公式中d表示日期中的日數,m表示月份數,y表示年數。注意 在公式中有個與其他公式不同的地方 判斷日期是該月的第幾周 datetime.now.da...
基姆拉爾森公式 判斷星期幾
時間限制 2000 ms 記憶體限制 65535 kb 難度 2 描述 acm的iphxer經常忘記某天是星期幾,但是他記那天的具體日期,他希望你能寫個程式幫幫他。輸入 每行有三個整數 year,month,day,日期在1600年1月1日到9600年1月1日之間 輸出輸出對應的星期,用乙個整數表示...
基姆拉爾森計算公式 推導
給定乙個 x xx xx日期,計算為星期幾。int y 年 int m 月 int d 日 int w 週幾從 公元0年1月1日星期日 開始 w d 1 7 公式 1 w d 1 y 7 公式 2 y 4 y 100 y 400結合之前的公式1,2 w d 1 y y 1 4 y 1 100 y 1...