星期幾怎麼算——牛逼公式

FROM http://bbs.csdn.net/topics/10163840

FROM atlantis13579

設D = (N, M, d)  (年, 月, 日)
把M減去2,如果小於0,就加上12,得到的數記爲m
如果M>=3,記n=N
如果M<=2,記n=N-1

用歐幾里德除法
得c, y 使: n = 100c + y, (0<=y<100)

則 W = d + [13m/5] + y + [y/4] + [c/4] - 2c   (mod 7)

則就是星期
星期天=0
星期一=1
....

例如:
D = (2002, 5, 8) 
則m=3
n=8
d=8
c=20
y=2
  
則W = 8 + [13*3/5] + 2 + [2/4] + [20/4] - 40  (mod 7)
    = -18 (mod 7) = 3 (mod 7)
  是星期三!


注:
  本公式適用於1582年10月15日之後, 因爲羅馬教皇格里高利十三世在這一天啓用新曆法.
(英國及其殖民地直到1752年9月14日才使用現在的歷法)

FROM andrew80

再談星期的計算


“讓我們看看1752年9月14號這個星期四吧,我們的公式最遠只能推算到這裏了。”
              ——Kim S. Larsen

“從公元元年1月1日開始到現在,每一天都是連續的。”
                                         ——於鵬

“西方曆法的第一次改革是羅馬朱利烏斯·凱撒大帝引進的。他採用的四年一閏的閏年方式。由於一個太陽年不剛好是365.25天,而是 365.242199…天。到16世紀,每年11分14秒的誤差已經累積成10天,也就是曆法上多了10天。於是教皇格利戈裏八世進行了一次校正。他在1582年2月24日以教皇訓令頒佈,將1582年10月5日至14日抹掉,並且對原來的閏年方法進行了校正。經過校正的歷法叫格利戈裏曆法,也就是我們現在用的公曆。1752年,英國人決定採用格利戈裏曆法,不過從1582年到那時,曆法又多出了1天,所以英國議會在1752年作出決定,抹掉11天——1752年9月3日至13日。”

日期的限制是Kim S. Larsen算法的問題嗎?不。
公元元年1月一日開始到現在,每一天都是連續的嗎?不。
一個簡單的方法就可以證明上述事實——用Linux的cal命令。啓動你的Linux在#提示符下輸入
cal  9  1752
你會看到:
    September 1752
    Su  Mo  Tu  We  Th  Fr  Sa
              1   2   14  15  16
     17  18  19  20   21  22  23
24  25  26  27   28  29  30
有趣吧一個只有19天的九月。
讓我們來看看這兩個算法,Kim S. Larsen博士的算法和於鵬同學的算法在本質上其實是相同的。只不過在實現的細節上略有不同。如果讓兩個算法去計算同一天(無論在1752年9月14日之前還是之後)是星期幾,二者的答案肯定是相同的。讓我們來分析一下吧。
首先,他們把日期對星期的決定作用都分爲年、月、日三個決定因素。對於年的因素,從兩者的計算公式  就能看出是相同的;對於日的因素,兩者都是直接計入,故也是相同的;而對於月的因素,Kim S. Larsen博士構造了一個公式,(一個非常巧妙的公式,)通過以月份爲自變量算出的函數值作爲對星期的影響量。而於鵬同學採用了查表的方法,即先構造好一個以月份爲索引的表對於相應的月份,通過查表得出其對星期的影響量。(以switch語句實現)不妨作如下演算:(爲了一致起見,採用一、二月作爲上年的十三、十四月。這是一個非常聰明的方法。)用於鵬同學的方法建表,並對7取模(表一)。再建立Kim S. Larsen函數 的函數值表(表二)。很顯然二者是相同的。


三月  0  0 三月  0
四月 31  3 四月  3
五月 61  5 五月  5
六月 92  1 六月  1
七月 122  3 七月  3
八月 153  6 八月  6
九月 184  2 九月  2
十月 214  4 十月  4
十一月 245  0 十一月  0
十二月 275  2 十二月  2
十三月 306  5 十三月  5
十四月 337  1 十四月  1

               表一                            表二 

其次,在處理閏年2月29日的問題上,兩者的做法略有不同,但效果還是相同的。Kim S. Larsen博士採用的方法相當高明,他把二月排在一年的最後,管他閏不閏,反正是最後一天。而於鵬同學加了一個if分支,直觀有效。
大師不愧爲大師,設計的算法簡潔、優美;而於鵬同學的算法,簡單易懂,並且效率並不差。
好了,該解決這個“歷史遺留問題”了。其實,並沒有什麼數學公式能算出指定日期是星期幾,我們可以試着拼湊一個,不過何必呢?加個if分枝不就解決問題了嗎?(Kim S. Larsen算法+於鵬思想)對Kim S. Larsen 博士的程序作一些必要的添加,可得到突破1752年9月14日日期限制的C語言程序。
/*C++Builder5下編譯通過*/
/*假設輸入的是正確的日期*/
#include <stdio.h>
char *name[] = { "Monday",
              "Tuesday",
              "Wednesday",
              "Thursday",
              "Friday",
              "Saturday",
              "Sunday"
               };
void main(){
  int D,M,Y,A;
  printf("Day: "); fflush(stdout);
  scanf("%d",&D);
  printf("Month: "); fflush(stdout);
  scanf("%d",&M);
  printf("Year: "); fflush(stdout);
  scanf("%d",&Y);
  if ((M == 1) || (M == 2)){/*一月、二月當作前一年的十三、十四月*/
    M += 12;
    Y--;
  }
  if ((Y < 1752)||((Y == 1752)&&(M < 9))
             ||((Y == 1752)&&(M == 9)&&(D < 3)))/*判斷是否在1752年9月3日前*/
    A = (D + 2*M + 3*(M+1)/5 + Y + Y/4 +5) % 7;/*1752年9月3日前的公式*/
  else A = (D + 2*M + 3*(M+1)/5 + Y + Y/4 - Y/100 + Y/400) % 7;/*1752年9月3日後的公式*/
  printf("It's a %s.\n",name[A]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章