[轉帖]快速計算某個日期是星期幾的經驗公式 - 時間,戛然而止 - CSDN博客

巧算星期幾

基姆。拉爾森

基姆擁有計算機學科的博士學位。他對資料庫,演算法和數據結構有著濃厚的興趣。他的聯繫地址是 (原文

為丹麥文--譯者注) 31,DK-5270,Odense N,Denmark,或發 E-mail 至:kslarsen@imada.ou.dk。

簡介

布魯斯 施耐爾

「四,六,九,十一,三十天就齊……」兒歌是這麼唱的;或許你也曾經掰著手指頭翻來覆去地數,讓趕上單數的指頭

代表只有30天的短月吧?這樣的口訣對我們是很管用的(我就是念叨著這首傻乎乎的兒歌長大的),可是電腦就沒有這

份「靈感」了。當然,我們可以用一大堆IF-THEN-ELSES的語句或幾個CASE來編寫計算程序,讓它計算某個指定日期是

星期幾。

不過我更喜歡基姆拉爾森在本月的「演算法小徑」中為我們帶來的新技巧,因為他的方法另闢蹊徑,從一個全新的方向著

手解決日期計算的問題。其實,並沒有什麼數學公式能算出某個指定日期是星期幾,不過我們可以試著拼湊一個,如果

我們的嘗試成功了,你就能擁有一個易於編程的數學公式,並能用它自動計算哪天是星期幾了。

順便說一句,如果你已經設計出更巧妙的演算法,或是在已有的方法上有了新突破的話,不妨告訴我,我一定洗耳恭聽。

我的聯繫方法是schneier@chinet.com,或者在DJJ編輯部給我留張便條就行。

你有沒有疑惑過你的電腦怎麼就知道今天是星期三呢?就算你的電腦關機了,你重啟後設定了新日期,它也能立即知道

這天是星期幾。

在你還是個孩子的時候,你可能見過一種紀錄記錄著年,月,日的表格,只要加上幾個數字,和它相連的另一張表格就

會告訴你這個日期是星期幾。當然,計算機硬碟的操作系統里也可以加入這樣的計算表。不過有一種簡單的方法可以輕

松地算出某天是星期幾;而且這個方法只佔用很少的內存空間,而那些只能推算幾百年的表格可就太佔地方了。

如果目前你的電腦還不具備推算與日期對應的星期數的功能,現在就不妨在自己的程序中試試下面的公式。

創建公式

首先,我們要用變數D,M和Y來表示日期。比如,1994年3月1日就用「D=1,M=3,Y=4」記錄。我們的目標是讓計算結

果在0到6之間。0代表星期一,1代表星期二,2代表星期三,依此類推。

1994年3月1日是個星期二,那麼「D mod 7(日期變數除以7的餘數))))」這個公式對於整個三月份都有效。比如3月18日

是星期五,18 mod 7=4;而4正代表星期五。別忘了,整數的除法和求模有著密切的關係。比方說,26除以7商3餘5,這

就是說,26除以7商數取整等於3,而26除以7求模(簡寫為26 mod 7)等於5。以上這些意味著19 mod 7=12 mod 7= 5

mod 7=5。在運算規則中,負數求模運演算法相似,所以依此類推,-2 mod 7=5, -9 mod 7=5。

在更正式的表達法中,統一用任意整數n和k表達上述關係,那麼這個過程可以表達為n=qk+r,這裡的q和r的取值範圍同

樣是整數和0。表1中列出了所有月份的變換數據(shift information此處試譯為「檔級數據」,還請進一步校對--譯

者注)。為了儘可能地得出規律,二月被排在最後,同理,一月也是如此。

例1(a)中的公式是仿照表1中的變換數據欄所描述的模式而創建的。這個公式中的除法一律是商數取整。所以得數是最

接近真正商數的整數。表2得出了此功能得出的有趣的數值。憑直覺,我們不難發現,當M(代表月份的變數)的值以1為單

位遞增時,2M就成倍增長,而3(M+1)/5就以3/5為增長倍數。

這正是我們仿製3,2,3,2,3這個重複格式所需要的(表中右邊的彎括弧表明了這一點)。請注意,我們在以7為除數求模

,那麼從6到2的求模結果就會逐個增加3(順序是6,0,1,2)。

現在,我們發現了適用於逐月向下推算的校正方法,並希望把它加入剛才的嘗試中,就是那個mod7公式。還以1994年3月

1日為例,這個日期的M=3。請注意,在例1(b)中,8 mod 7=1,所以當整個公式合併時,必須減去1。在做以7為除數

求模的運算時,減1和加6是一樣的,因為-1 mod 7=6 mod 7=6。

這樣,例1(c)中的公式就可以計算這一年中剩下的月份了。其實,既然我們把一月和二月排在表1的最後,那麼只要我

們把它們看成是十三月和十四月,就能接著推算1995年的前兩個月了。這是因為,雖然它們並不是一個完整的3,2,3,2,3

結構,但恰好可以是這個結構的開始,為了使這個公式更完善,我們還是最好把一月和二月看成是上一年的十三月和十

四月。

加入年份

順著年份向下找,我們觀察到1995年3月1日是星期三。這說明,每增加一年,我們公式的計算結果就會增加1。這太簡單

了,我們只要簡單地把年份加上去就行了。再提醒你一次,我們必須確保出發點是正確的。由於1994 mod 7=6,我們在

把Y加入已有的公式時就必須減去6。由此改進的例2(a)就更完善了。

1996年是個閏年,這帶來了我們的下一個問題。這一年的3月1日是星期五,而不是剛才的公式推算出的星期四。所以每

當我們碰上閏年時還得多加上1。判斷閏年的規則是,能被4整除,並能被100和400同時整除的年份就是閏年。就這樣,

我們在原有的基礎上添加Y/4--Y/100+Y/400。再強調一下,我們必須從一開始就確保正確。既然(1994/4--

1994/100+1994/400) mod 7=(498--19+4) mod 7=483 mod7=0,所以就不用再做任何調整了。這樣,例2(b)就是我們最終

的成果了。這個公式能一直工作下去,除非改變現行的日曆系統。作為示例,讓我們試著推算一下2000年7月4日:

(4+2*3+(7+1)/5+2000+2000/4--2000/100+2000/400) mod 7=(4+14+2000+500--20+5) mod 7=2507 mod 7=1,所以那一

天是星期二。

這個公式還能推算過去的日期;然而計算範圍有限,讓我們看看1752年9月14號這個星期四吧,我們的公式最遠只能推算

到這裡了。不過像「1963年11月22日你在哪裡」這樣的日常問題中提到的日期還是可以輕鬆應對的:(22+2*11+3

(11+1)/5+1963+1963/4--1963/100+1963/400) mod7=(22+22+7+1963+490--19+4) mod 7=2489 mod 7=4。那天就是星期

五。

例3例子3是一個C語言程序,按照把這個公式自動推算給定日期是星期幾。

表1:每月變換數據

月份 天數 變換

三月 31 3

四月 30 2

五月 31 3

六月 30 2

七月 31 3

八月 31 3

九月 30 2

十月 31 3

十一月 30 2

十二月 31 3

一月 31 3

二月 28 3

表2:仿製變換數據形式的功能。例1中建立的公式可以適用於1994年。例2把這個公式的功能擴展到可以應用在不同的年

份進行推算。

例3:用C語言程序表達上述公式

  1. /*一 月和二月被當作前一年的*/
  2. /*十三月和十四月分別處理*/
  3. /*計算指定日期是星期幾。默認輸入的*/
  4. /*數字代表正確的日期*/
  5. /* 推算給定日期是星期幾,假定輸入是正確的數據*/
  6. #include
  7. char*name[]={"Monday",
  8. "Tuesday",
  9. "Wednesday",
  10. "Thursday",
  11. "Friday",
  12. "Saturday",
  13. "Sunday"
  14. };
  15. voidmain()
  16. {
  17. intD,M,Y,A;
  18. printf("Day:");fflush(stdout);
  19. scanf("%d",&D);
  20. printf("Month:");fflush(stdout);
  21. scanf("%d",&M);
  22. printf("Year:");fflush(stdout);
  23. scanf("%d",&Y);
  24. /*JanuaryandFebruaryaretreatedasmonth13and14*/
  25. /*respectively,fromtheyearbefore.*/
  26. if((M==1)||(M==2))
  27. {
  28. M+=12;
  29. Y--;
  30. }
  31. A=(D+2*M+3*(M+1)/5+Y+Y/4-Y/100+Y/400)%7;
  32. printf("It"sa%s.
    ",name[A]);
  33. }
  34. /*一月和二月被當作前一年的*/

推薦閱讀:

CSDN是不是越來越垃圾了?
有哪些網站有嚴格的隱私保護?
五十六中華民族 - kenny - CSDNBlog
CSDN 融資千萬美元,在中國效仿 Linkedin 有機會嗎?

TAG:經驗 | 公式 | 博客 | 時間 | 計算 | 日期 | 星期 | CSDN | 經驗公式 |