日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

算法系列之十七:日歷生成算法

 任萬廷 2014-05-05

【接上篇】

 

        上述計(jì)算星期的方法雖然步驟簡(jiǎn)單,但是每次都要計(jì)算兩個(gè)日期的時(shí)間差,不是非常方便。如果能夠有一個(gè)公式可以直接根據(jù)日期計(jì)算出對(duì)應(yīng)的星期豈不是更好?幸運(yùn)的是,這樣的公式是存在的。此類公式的推導(dǎo)原理仍然是通過兩個(gè)日期的時(shí)間差來計(jì)算星期,只是通過選擇一個(gè)特殊的日期來簡(jiǎn)化公式的推導(dǎo)。這個(gè)所謂的特殊日期指的是某一年的1231日這天剛好是星期日這種情況。選擇這樣的日子有兩個(gè)好處,一個(gè)是計(jì)算上可以省去計(jì)算標(biāo)準(zhǔn)日期這一年的剩余天數(shù),另一個(gè)是計(jì)算出來的日期差余數(shù)是幾就是星期幾,不需要再計(jì)算星期的差值。人們知道公元元年的11日是星期一,那么公元前1年的1231日就是星期日,用這一天作為標(biāo)準(zhǔn)日期,就可以只計(jì)算整數(shù)年的時(shí)間和日期所在的年積累的天數(shù),這個(gè)星期公式就是:

 

w = (L * 366 + N * 365 + D) % 7                             (公式 2)

 

公式中的L是從公元元年到ymd日所在的年之間的閏年次數(shù),N是平常年次數(shù),Dy年內(nèi)的積累天數(shù)。將整年數(shù)y - 1 = L + N帶入上式,可得:

 

w = ( (y - 1) * 365 + L + D) % 7                              (公式 3)

 

根據(jù)閏年規(guī)律,從公元元年到y年之間的閏年次數(shù)是可以計(jì)算出來的,即:

L帶入公式2,得到星期w的最終計(jì)算公式:

還以2005531日為例,利用公式5計(jì)算w的值為:

得到2005531日是星期二,和前面的計(jì)算方法得到的結(jié)果一致。根據(jù)上述分析,可得寫出使用公式5計(jì)算星期的算法實(shí)現(xiàn):

146 int TotalWeek(int year, int month, int day)

147 {

148     int d = CalcYearPassedDays(year, month, day);

149     int y = year - 1;

150     int w = y * DAYS_OF_NORMAL_YEAR + y / 4 - y / 100 + y / 400 + d;

151 

152     return w % 7;

153 }

        公式5的問題在于計(jì)算量大,不利于口算星期結(jié)果。于是人們就在公式5的基礎(chǔ)上繼續(xù)推導(dǎo)更簡(jiǎn)單的公式。德國(guó)數(shù)學(xué)家克里斯蒂安·蔡勒(Christian Zeller, 1822- 1899)在1886年推導(dǎo)出了著名的為蔡勒(Zeller)公式:

 

對(duì)計(jì)算出的w值除以7,得到的余數(shù)就是星期幾,如果余數(shù)是0,則為星期日。蔡勒公式中各符號(hào)的含義如下:

w :星期;

:世紀(jì)數(shù) – 1的值,如21世紀(jì),則 = 20;

:月數(shù),的取值是大于等于3,小于等于14。在蔡勒公式中,某年的1月和2月看作上一年的13月和14月,比如200121日要當(dāng)成2000年的141日計(jì)算;

:年份,取公元紀(jì)念的后兩位,如1998年, = 98,2001年, = 1;

d :某月內(nèi)的日數(shù)

 

為了方便口算,人們通常將公式6中的一項(xiàng)改成

。目前人們普遍認(rèn)為蔡勒公式是計(jì)算某一天是星期幾的最好的公式。但是蔡勒公式有時(shí)候可能計(jì)算出的結(jié)果是負(fù)數(shù),需要對(duì)結(jié)果+7進(jìn)行修正。比如200671日,用蔡勒公式計(jì)算出的結(jié)果是 -1,實(shí)際上這天是星期六。根據(jù)前面分析的結(jié)果整理出的蔡勒公式算法實(shí)現(xiàn)如下:

155 int ZellerWeek(int year, int month, int day)

156 {

157     int m = month;

158     int d = day;

159 

160     if(month <= 2) /*對(duì)小于2的月份進(jìn)行修正*/

161     {

162         year--;

163         m = month + 12;

164     }

165 

166     int y = year % 100;

167     int c = year / 100;

168 

169     int w = (y + y / 4 + c / 4 - 2 * c + (13 * (m + 1) / 5) + d - 1) % 7;

170     if(w < 0) /*修正計(jì)算結(jié)果是負(fù)數(shù)的情況*/

171         w += 7;

172 

173     return w;

174 }

 

        蔡勒公式(公式6)和前面提到的公式5都只適用于格里歷法。羅馬教皇在1582年修改歷法,將105日指定為1015日,從而正式廢止儒略歷法,開始啟用格里歷法。因此,上述求星期幾的公式只適用于15821015日之后的日期,對(duì)于1582年將104日之前的日期,蔡勒也推導(dǎo)出了適用與儒略歷法的星期計(jì)算公式:

公式7適用于對(duì)1582104日之前的日期計(jì)算星期,1582105日與15821015日之間的日期是不存在的,因?yàn)樗鼈兌际峭惶臁?/p>

 

        格里歷歷法簡(jiǎn)單,除二月外每月天數(shù)固定,二月則根據(jù)是否是閏年確定是28天還是29天,每天的星期數(shù)可以通過蔡勒公式(公式6)計(jì)算,有了這些信息,就可以按照一定的排版格式將某一年的日歷打印出來。排版打印的算法非常簡(jiǎn)單,就是按照順序打印12個(gè)月的月歷,因此,打印月歷的函數(shù)就是輸出算法的重點(diǎn)。代碼沒什么特別之處,就是用一些小技巧確定每個(gè)月的第一天的開始位置,打印月歷的核心代碼如下:

229 void PrintMonthCalendar(int year, int month)

230 {

231     int days = GetDaysOfMonth(year, month); /*確定這個(gè)月的天數(shù)*/

232     if(days <= 0)

233         return;

234 

235     PrintMonthBanner(nameOfMonth[month - 1]);

236     PrintWeekBanner();

237     int firstDayWeek = ZellerWeek(year, month, 1);

238     InsertRowSpace(firstDayWeek);

239     int week = firstDayWeek;

240     int i = 1;

241     while(i <= days)

242     {

243         printf("%-10d", i);

244         if(week == 6) /*到一周結(jié)束,切換到下一行輸出*/

245         {

246             SetNextRowStart();

247         }

248         i++;

249         week = (week + 1) % 7;

250     }

251 }

 

GetDaysOfMonth()函數(shù)其實(shí)就是從daysOfMonth表中查一下每月的天數(shù),如果是閏年,則對(duì)二月的天數(shù)修正(+1),daysOfMonth表定義如下:

 

int daysOfMonth[MONTHES_FOR_YEAR] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

 

計(jì)算星期不必對(duì)每一天都計(jì)算一次,只要對(duì)每個(gè)月的第一天計(jì)算一次就可以了,以后的日期可以用 week = (week + 1) % 7 直接推算出星期幾。下面就是我們的算法打印輸出的效果:

 

********************************************************************************

 

                              Calendar of 2012

 

********************************************************************************

 

----------January----------

 

Sunday    Monday    Tuesday   Wednesday Thursday  Friday    Saturday

1         2         3         4         5         6         7

8         9         10        11        12        13        14

15        16        17        18        19        20        21

22        23        24        25        26        27        28

29        30        31

 

----------February----------

 

Sunday    Monday    Tuesday   Wednesday Thursday  Friday    Saturday

                              1         2         3         4

5         6         7         8         9         10        11

12        13        14        15        16        17        18

19        20        21        22        23        24        25

26        27        28        29

 

----------March----------

 

Sunday    Monday    Tuesday   Wednesday Thursday  Friday    Saturday

                                        1         2         3

4         5         6         7         8         9         10

11        12        13        14        15        16        17

18        19        20        21        22        23        24

25        26        27        28        29        30        31

 

……

 

 

小知識(shí)2儒略歷和格里歷

在公元15821015日之前,人們使用的歷法是源自古羅馬的儒略歷,儒略歷的置閏規(guī)則就是四年一閏,但是沒有計(jì)算每年多出來的0.0078天,這樣從公元前46年到公元1582年一共累積多出了10天,為此,當(dāng)時(shí)的教皇格里十三世將1582105日人為指定為1015日,并開始啟用新的置閏規(guī)則,這就是后來沿用至今的格里歷。

 

 

小知識(shí)3約化儒略日

由于儒略日數(shù)字位數(shù)太多,國(guó)際天文聯(lián)合會(huì)于19738月決定對(duì)其修正,采用約化儒略日(MJD)進(jìn)行天文計(jì)算,定義MJD = JD – 2400000.5,MJD相應(yīng)的起始點(diǎn)是18581117 0:00。

 

 

小知識(shí)417529月到底是怎么回事兒

如果你用的操作系統(tǒng)是unixlinux,在控制臺(tái)輸入以下命令:

 

#cal 9 1752

 

你會(huì)看到這樣一個(gè)奇怪的月歷輸出:

 

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

 

1752年的9月缺了11天,到底怎么回事兒?這其實(shí)還是因?yàn)閺娜迓詺v到格里歷的轉(zhuǎn)換造成的。1582105日,羅馬教皇格里十三世宣布啟用更為精確的格里歷,但是整個(gè)歐洲大陸并不是所有國(guó)家都立即采用格里歷,比如大英帝國(guó)就是直到17529月議會(huì)才批準(zhǔn)采用格里歷,所以大英帝國(guó)及其所有殖民地的歷法一直到17529月才發(fā)生跳變,“跟上”了格里歷。德國(guó)和荷蘭到了1698年才采用格里歷,而俄羅斯則直到1918年革命才采用格里歷。Linuxcal指令起源與最初AT&TUNIX,當(dāng)然采用的是美國(guó)歷法,但是美國(guó)歷史太短,再往前就只能采用英國(guó)歷法,所以cal指令的結(jié)果就成了這樣。對(duì)于采用格里歷的國(guó)家來說,只要知道158210月發(fā)生了日期跳變就行了,可以不用關(guān)心17529月到底是怎么回事兒。但是對(duì)于研究歷史和考古的人來說,就必需要了解這個(gè)歷史,搞清楚每個(gè)歐洲國(guó)家改用格里歷的年份,否則就可能在一些問題上出錯(cuò)。在歐洲研究歷史,你會(huì)發(fā)現(xiàn)很多事件都是有多個(gè)時(shí)間版本的,比如大科學(xué)家牛頓的生日就有兩個(gè)時(shí)間版本,一個(gè)是按照儒略歷歷法的16421225日,另一個(gè)是格里歷歷法的164314日,對(duì)于英國(guó)人來說,1752年之前都是按照儒略歷計(jì)算的,所以英國(guó)的史書可能會(huì)記載牛頓出生在圣誕節(jié),這也沒什么可奇怪的。

 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多