日历查询的算法,如何确定某一天是星期几?

/***********************************************************************************

    引用的网址:

           基姆拉尔森公式推导:https://www.cnblogs.com/hanxi/archive/2012/06/12/2545828.html

           蔡勒公式推导:        https://www.cnblogs.com/carekee/articles/4529720.html

**********************************************************************************/


历史上的某一天是星期几?未来的某一天是星期几?关于这个问题,有很多计算公式。


一、—— 蔡勒(Zeller)公式 


   
计算星期几的算法中,最著名的是蔡勒(Zeller)公式。即w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1 

    公式中的符号含义如下,w:星期;c:世纪-1;y:年(两位数);m:月(m大于等于3,小于等于14,即在蔡勒公式中,某年的1、2月要看作上一年的13、14月来计算,比如2003年1月1日要看作2002年的13月1日来计算);d:日;[ ]代表取整,即只要整数部分。(C是世纪数减一,y是年份后两位,M是月份,d是日数。1月和2月要按上一年的13月和 14月来算,这时C和y均按上一年取值。) 

    算出来的W除以7,余数是几就是星期几。如果余数是0,则为星期日。 

    以2049年10月1日(100周年国庆)为例,用蔡勒(Zeller)公式进行计算,过程如下: 
蔡勒(Zeller)公式:w=y+[y/4]+[c/4]-2c+[26(m+1)/10]+d-1 
=49+[49/4]+[20/4]-2×20+[26× (10+1)/10]+1-1 
=49+[12.25]+5-40+[28.6] 
=49+12+5-40+28 
=54 (除以7余5) 
即2049年10月1日(100周年国庆)是星期5。 

你的生日(出生时、今年、明年)是星期几?不妨试一试。 

不过,以上公式只适合于1582年10月15日之后的情形(当时的罗马教皇将恺撒大帝制订的儒略历修改成格里历,即今天使用的公历)。 


二、—— 基姆拉尔森(Kim larsson calculation formula)计算公式 

计算公式: W= (d+2*m+3*(m+1)/5+y+y/4-y/100+y/400) mod 7

    在公式中d表示日期中的日数,m表示月份数,y表示年数。


注意:把一月和二月看成是上一年的十三月和十四月,例:如果是2004-1-10则换算成:2003-13-10来代入公式计算。



过程的推导:(对推理不感兴趣的可略过不看) 

1、蔡勒公式
    星期制度是一种有古老传统的制度。据说因为《圣经·创世纪》中规定上帝用了六天时间创世纪,第七天休息,所以人们也就以七天为一个周期来安排自己的工作和生 活,而星期日是休息日。从实际的角度来讲,以七天为一个周期,长短也比较合适。所 以尽管中国的传统工作周期是十天(比如王勃《滕王阁序》中说的“十旬休暇”,即是 指官员的工作每十日为一个周期,第十日休假),但后来也采取了西方的星期制度。 

    在日常生活中,我们常常遇到要知道某一天是星期几的问题。有时候,我们还想知道历史上某一天是星期几。通常,解决这个方法的有效办法是看日历,但是我们总不会 随时随身带着日历,更不可能随时随身带着几千年的万年历。假如是想在计算机编程中 计算某一天是星期几,预先把一本万年历存进去就更不现实了。这时候是不是有办法通 过什么公式,从年月日推出这一天是星期几呢? 

答案是肯定的。其实我们也常常在这样做。我们先举一个简单的例子。比如,知道了2004年5月1日是星期六,那么2004年5月31日“世界无烟日”是星期几就不难推算出来。我们可以掰着指头从1日数到31日,同时数星期,最后可以数出5月31日是星期一。 
其实运用数学计算,可以不用掰指头。我们知道星期是七天一轮回的,所以5月1日是星期六,七天之后的5月8日也是星期六。在日期上,8-1=7,正是7的倍数。同样,5月15日、5月22日和5月29日也是星期六,它们的日期和5月1日的差值分别是14、21和28,也都是7的倍数。那么5月31日呢?31-1=30,虽然不是7的倍数,但是31除以7,余数为2, 
这就是说,5月31日的星期,是在5月1日的星期之后两天。星期六之后两天正是星期一。 

这个简单的计算告诉我们计算星期的一个基本思路:首先,先要知道在想算的日子之前的一个确定的日子是星期几,拿这一天做为推算的标准,也就是相当于一个计算的“原点”。其次,知道想算的日子和这个确定的日子之间相差多少天,用7除这个日期的差值,余数就表示想算的日子的星期在确定的日子的星期之后多少天。如果余数是0,就表示这两天的星期相同。显然,如果把这个作为“原点”的日子选为星期日,那 
么余数正好就等于星期几,这样计算就更方便了。 

但是直接计算两天之间的天数,还是不免繁琐。比如1982年7月29日和2004年5月1日之间相隔7947天,就不是一下子能算出来的。它包括三段时间:一,1982年7月29日以后这一年的剩余天数;二,1983-2003这二十一个整年的全部天数;三,从2004年元旦到5月1日经过的天数。第二段比较好算,它等于21*365+5=7670天,之所以要加5,是因为这段时间内有5个闰年。第一段和第三段就比较麻烦了,比如第三段,需要把5月之前的四个月的天数累加起来,再加上日期值,即31+29+31+30+1=122天。同理,第 
一段需要把7月之后的五个月的天数累加起来,再加上7月剩下的天数,一共是155天。 
所以总共的相隔天数是122+7670+155=7947天。 

仔细想想,如果把“原点”日子的日期选为12月31日,那么第一段时间也就是一个整年,这样一来,第一段时间和第二段时间就可以合并计算,整年的总数正好相当于两个日子的年份差值减一。如果进一步把“原点”日子选为公元前1年12月31日(或者天文学家所使用的公元0年12月31日),这个整年的总数就正好是想算的日子的年份减一。这样简化之后,就只须计算两段时间:一,这么多整年的总天数;二,想算的日子是这一年的第几天。巧的是,按照公历的年月设置,这样反推回去,公元前1年12月31日正好是星期日,也就是说,这样算出来的总天数除以7的余数正好是星期几。那么现在的问题就 
只有一个:这么多整年里面有多少闰年。这就需要了解公历的置闰规则了。 

我们知道,公历的平年是365天,闰年是366天。置闰的方法是能被4整除的年份在2月加一天,但能被100整除的不闰,能被400整除的又闰。因此,像1600、2000、2400年都是闰年,而1700、1800、1900、2100年都是平年。公元前1年,按公历也是闰年。 

因此,对于从公元前1年(或公元0年)12月31日到某一日子的年份Y之间的所有整年中的闰年数,就等于 
[(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400], 

[...]表示只取整数部分。第一项表示需要加上被4整除的年份数,第二项表示需要去掉被100整除的年份数,第三项表示需要再加上被400整除的年份数。之所以Y要减一,这 
样,我们就得到了第一个计算某一天是星期几的公式: 

W = (Y-1)*365 + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D. (1) 

其中D是这个日子在这一年中的累积天数。算出来的W就是公元前1年(或公元0年)12月31日到这一天之间的间隔日数。把W用7除,余数是几,这一天就是星期几。比如我们来算2004年5月1日: 

W = (2004-1)*365 + [(2004-1)/4] - [(2004-1)/100] + [(2004-1)/400] +(31+29+31+30+1)= 731702, 
731702 / 7 = 104528……6,余数为六,说明这一天是星期六。这和事实是符合的。 

上面的公式(1)虽然很准确,但是计算出来的数字太大了,使用起来很不方便。仔细想想,其实这个间隔天数W的用数仅仅是为了得到它除以7之后的余数。这启发我们是不是可以简化这个W值,只要找一个和它余数相同的较小的数来代替,用数论上的术语来说,就是找一个和它同余的较小的正整数,照样可以计算出准确的星期数。 

显然,W这么大的原因是因为公式中的第一项(Y-1)*365太大了。其实, 

(Y-1)*365 = (Y-1) * (364+1) 
= (Y-1) * (7*52+1) 
= 52 * (Y-1) * 7 + (Y-1), 

这个结果的第一项是一个7的倍数,除以7余数为0,因此(Y-1)*365除以7的余数其实就等于Y-1除以7的余数。这个关系可以表示为: 

(Y-1)*365 ≡ Y-1 (mod 7). 

其中,≡是数论中表示同余的符号,mod 7的意思是指在用7作模数(也就是除数)的情况下≡号两边的数是同余的。因此,完全可以用(Y-1)代替(Y-1)*365,这样我们就得到了那个著名的、也是最常见到的计算星期几的公式: 

W = (Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + D. (2) 

这个公式虽然好用多了,但还不是最好用的公式,因为累积天数D的计算也比较麻 
烦。是不是可以用月份数和日期直接计算呢?答案也是肯定的。我们不妨来观察一下各 
个月的日数,列表如下: 

月 份:1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月 
-------------------------------------------------------------------------- 
天 数: 31 28(29) 31 30 31 30 31 31 30 31 30 31 

如果把这个天数都减去28(=4*7),不影响W除以7的余数值。这样我们就得到另一张 
表: 

月 份:1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月 
------------------------------------------------------------------------ 
剩余天数: 3 0(1) 3 2 3 2 3 3 2 3 2 3 
平年累积: 3 3 6 8 11 13 16 19 21 24 26 29 
闰年累积: 3 4 7 9 12 14 17 20 22 25 27 30 

仔细观察的话,我们会发现除去1月和2月,3月到7月这五个月的剩余天数值是3,2,3,2,3;8月到12月这五个月的天数值也是3,2,3,2,3,正好是一个重复。相应的累积天数中, 后一月的累积天数和前一月的累积天数之差减去28就是这个重复。正是因为这种规律的 存在,平年和闰年的累积天数可以用数学公式很方便地表达: 

╭ d; (当M=1) 
D = { 31 + d; (当M=2) (3) 
╰ [ 13 * (M+1) / 5 ] - 7 + (M-1) * 28 + d + i. (当M≥3) 

其中[...]仍表示只取整数部分;M和d分别是想算的日子的月份和日数;平年i=0,闰年 i=1。对于M≥3的表达式需要说明一下:[13*(M+1)/5]-7算出来的就是上面第二个表中的 平年累积值,再加上(M-1)*28就是想算的日子的月份之前的所有月份的总天数。这是一 个很巧妙的办法,利用取整运算来实现3,2,3,2,3的循环。比如,对2004年5月1日,有: 

D = [ 13 * (5+1) / 5 ] - 7 + (5-1) * 28 + 1 + 1 
= 122, 

这正是5月1日在2004年的累积天数。 

假如,我们再变通一下,把1月和2月当成是上一年的“13月”和“14月”,不仅仍 然符合这个公式,而且因为这样一来,闰日成了上一“年”(一共有14个月)的最后一 天,成了d的一部分,于是平闰年的影响也去掉了,公式就简化成: 

D = [ 13 * (M+1) / 5 ] - 7 + (M-1) * 28 + d. (3≤M≤14) (4) 

上面计算星期几的公式,也就可以进一步简化成: 

W = (Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + [ 13 * (M+1) / 5 ] - 7 + (M-1) * 28 + d. 

因为其中的-7和(M-1)*28两项都可以被7整除,所以去掉这两项,W除以7的余数不变, 公式变成: 

W = (Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] + [ 13 * (M+1) / 5 ] + d.(5) 

当然,要注意1月和2月已经被当成了上一年的13月和14月,因此在计算1月和2月的日子 的星期时,除了M要按13或14算,年份Y也要减一。比如,2004年1月1日是星期四,用这个公式来算,有: 

W = (2003-1) + [(2003-1)/4] - [(2003-1)/100] + [(2003-1)/400] + [13*(13+1)/5] 
+ 1 
= 2002 + 500 - 20 + 5 + 36 + 1 
= 2524; 
2524 / 7 = 360……4.这和实际是一致的。 

公式(5)已经是从年、月、日来算星期几的公式了,但它还不是最简练的,对于年份的处理还有改进的方法。我们先来用这个公式算出每个世纪第一年3月1日的星期,列表如下: 

年份: 1(401,801,…,2001) 101(501,901,…,2101) 
-------------------------------------------------------------------- 
星期: 4 2 
============================================== 
年份:201(601,1001,…,2201) 301(701,1101,…,2301) 
-------------------------------------------------------------------- 
星期: 0 5 

可以看出,每隔四个世纪,这个星期就重复一次。假如我们把301(701,1101,…,2301)年3月1日的星期数看成是-2(按数论中对余数的定义,-2和5除以7的余数相同,所以可以做这样的变换),那么这个重复序列正好就是一个4,2,0,-2的等差数列。据此,我们 可以得到下面的计算每个世纪第一年3月1日的星期的公式: 

W = (4 - C mod 4) * 2 - 4. (6) 

式中,C是该世纪的世纪数减一,mod表示取模运算,即求余数。比如,对于2001年3月1日,C=20,则: 

W = (4 - 20 mod 4) * 2 - 4 
= 8 - 4 
= 4. 

把公式(6)代入公式(5),经过变换,可得: 

(Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400] ≡ (4 - C mod 4) * 2 - 1 
(mod 7). (7) 

因此,公式(5)中的(Y-1) + [(Y-1)/4] - [(Y-1)/100] + [(Y-1)/400]这四项,在计算每个世纪第一年的日期的星期时,可以用(4 - C mod 4) * 2 - 1来代替。这个公式写出来就是: 

W = (4 - C mod 4) * 2 - 1 + [13 * (M+1) / 5] + d. (8) 

有了计算每个世纪第一年的日期星期的公式,计算这个世纪其他各年的日期星期的公式就很容易得到了。因为在一个世纪里,末尾为00的年份是最后一年,因此就用不着再考虑“一百年不闰,四百年又闰”的规则,只须考虑“四年一闰”的规则。仿照由公式(1)简化为公式(2)的方法,我们很容易就可以从式(8)得到一个比公式(5)更简单的计算任意一天是星期几的公式: 

W = (4 - C mod 4) * 2 - 1 + (y-1) + [y/4] + [13 * (M+1) / 5] + d. (9) 

式中,y是年份的后两位数字。 

如果再考虑到取模运算不是四则运算,我们还可以把(4 - C mod 4) * 2进一步改写成只含四则运算的表达式。因为世纪数减一C除以4的商数q和余数r之间有如下关系: 

4q + r = C, 

其中r即是 C mod 4,因此,有: 

r = C - 4q 
= C - 4 * [C/4]. (10) 

则 

(4 - C mod 4) * 2 = (4 - C + 4 * [C/4]) * 2 
= 8 - 2C + 8 * [C/4] 
≡ [C/4] - 2C + 1 (mod 7). (11) 

把式(11)代入(9),得到: 

W = [C/4] - 2C + y + [y/4] + [13 * (M+1) / 5] + d - 1. (12) 

这个公式由世纪数减一、年份末两位、月份和日数即可算出W,再除以7,得到的余数是几就表示这一天是星期几,唯一需要变通的是要把1月和2月当成上一年的13月和14月, C和y都按上一年的年份取值。因此,人们普遍认为这是计算任意一天是星期几的最好的公式。这个公式最早是由德国数学家克里斯蒂安·蔡勒(Christian Zeller, 1822-1899)在1886年推导出的,因此通称为蔡勒公式(Zeller's Formula)。为方便口算,式中的[13 * (M+1) / 5]也往往写成[26 * (M+1) / 10]。 

现在仍然让我们来算2004年5月1日的星期,显然C=20,y=4,M=5,d=1,代入蔡勒 
公式,有: 

W = [20/4] - 40 + 4 + 1 + [13 * (5+1) / 5] + 1 - 1 
= -15. 

注意负数不能按习惯的余数的概念求余数,只能按数论中的余数的定义求余。为了方便 计算,我们可以给它加上一个7的整数倍,使它变为一个正数,比如加上70,得到55。 再除以7,余6,说明这一天是星期六。这和实际是一致的,也和公式(2)计算所得的结果一致。 

最后需要说明的是,上面的公式都是基于公历(格里高利历)的置闰规则来考虑的。对于儒略历,蔡勒也推出了相应的公式是: 

W = 5 - C + y + [y/4] + [13 * (M+1) / 5] + d - 1. (13) 

这样,我们终于一劳永逸地解决了不查日历计算任何一天是星期几的问题。


2、基姆拉尔森公式

下面我们完全按自己的思路由简单到复杂一步步进行推导…… 

推导之前,先作两项规定: 
①用   y,   m,   d,   w   分别表示   年   月   日   星期(w=0-6   代表星期日-星期六 
②我们从   公元0年1月1日星期日   开始 


(1)只考虑最开始的   7   天,即   d   =   1---7   变换到   w   =   0---6 
        很直观的得到: 
        w   =   d-1 

(2)扩展到整个1月份 
        模7的概念大家都知道了,也没什么好多说的。不过也可以从我们平常用的日历中看出来,在周历里边每列都是一个按7增长的等差数列,如1、8、15、22的星期都是相同的。所以得到整个1月的公式如下: 
        w   =   (d-1)   %   7     ---------   公式⑴ 

(3)按年扩展 
        由于按月扩展比较麻烦,所以将年扩展放在前面说 

        ①   我们不考虑闰年,假设每一年都是   365   天。由于365是7的52倍多1天,所以每一年的第一天和最后一天星期是相同的。 
        也就是说下一年的第一天与上一年的第一天星期滞后一天。这是个重要的结论,每过一年,公式⑴会有一天的误差,由于我们是从0年开始的,所以只须要简单的加上年就可以修正扩展年引起的误差,得到公式如下: 
        w   =   (d-1   +   y)   %   7   

        ②   将闰年考虑进去 
        每个闰年会多出一天,会使后面的年份产生一天的误差。如我们要计算2005年1月1日星期几,就要考虑前面的已经过的2004年中有多少个闰年,将这个误差加上就可以正确的计算了。 
        根据闰年的定义(能被4整但不能被100整除或能被400整),得到计算闰年的个数的算式:y/4   -   y/100   +   y/400。 
        由于我们要计算的是当前要计算的年之前的闰年数,所以要将年减1,得到了如下的公式: 
        w   =   [d-1+y   +   (y-1)/4-(y-1)/100+(y-1)/400]   %   7   -----公式⑵ 

        现在,我们得到了按年扩展的公式⑵,用这个公式可以计算任一年的1月份的星期 

(4)扩展到其它月 
        考虑这个问题颇费了一翻脑筋,后来还是按前面的方法大胆假才找到突破口。 

        ①现在我们假设每个月都是28天,且不考虑闰年 
        有了这个假设,计算星期就太简单了,因为28正好是7的整数倍,每个月的星期都是一样的,公式⑵对任一个月都适用   :) 

        ②但假设终究是假设,首先1月就不是28天,这将会造成2月份的计算误差。1月份比28天要多出3天,就是说公式⑵的基础上,2月份的星期应该推后3天。 
        而对3月份来说,推后也是3天(2月正好28天,对3月的计算没有影响)。 
        依此类推,每个月的计算要将前面几个月的累计误差加上。 
        要注意的是误差只影响后面月的计算,因为12月已是最后一个月,所以不用考虑12月的误差天数,同理,1月份的误差天数是0,因为前面没有月份影响它。 

        由此,想到建立一个误差表来修正每个月的计算。 
================================================== 
月     误差   累计     模7 
1       3         0           0 
2       0         3           3 
3       3         3           3 
4       2         6           6 
5       3         8           1 
6       2         11         4 
7       3         13         6 
8       3         16         2 
9       2         19         5 
10     3         21         0 
11     2         24         3 
12     -         26         5 
        (闰年时2月会有一天的误差,但我们现在不考虑) 
================================================== 

        我们将最后的误差表用一个数组存放 
        在公式⑵的基础上可以得到扩展到其它月的公式 

        e[]   =   {0,3,3,6,1,4,6,2,5,0,3,5} 
        w   =   [d-1+y   +   e[m-1]   +   (y-1)/4-(y-1)/100+(y-1)/400]   %   7   --公式⑶ 

        ③上面的误差表我们没有考虑闰年,如果是闰年,2月会一天的误差,会对后面的3-12月的计算产生影响,对此,我们暂时在编程时来修正这种情况,增加的限定条件是如果当年是闰年,且计算的月在2月以后,需要加上一天的误差。大概代码是这样的: 
        
        w   =   (d-1   +   y   +   e[m-1]   +   (y-1)/4   -   (y-1)/100   +   (y-1)/400); 
        if(m> 2   &&   (y%4==0   &&   y%100!=0   ||   y%400==0)   &&   y!=0) 
                ++w; 
        w   %=   7; 
        
        现在,已经可以正确的计算任一天的星期了。 
        注意:0年不是闰年,虽然现在大都不用这个条件,但我们因从公元0年开始计算,所以这个条件是不能少的。 

        ④   改进 
        公式⑶中,计算闰年数的子项   (y-1)/4-(y-1)/100+(y-1)/400   没有包含当年,如果将当年包含进去,则实现了如果当年是闰年,w   自动加1。 
        由此带来的影响是如果当年是闰年,1,2月份的计算会多一天误差,我们同样在编程时修正。则代码如下 
        
        w   =   (d-1   +   y   +   e[m-1]   +   y/4   -   y/100   +   y/400);   ----   公式⑷ 
        if(m <3   &&   (y%4==0   &&   y%100!=0   ||   y%400==0)   &&   y!=0) 
                --w; 
        w   %=   7; 
        
        与前一段代码相比,我们简化了   w   的计算部分。 
        实际上还可以进一步将常数   -1   合并到误差表中,但我们暂时先不这样做。 
        
        至此,我们得到了一个阶段性的算法,可以计算任一天的星期了。

    (5)简化 
        现在我们推导出了自己的计算星期的算法了,但还不能称之为公式。 
        所谓公式,应该给定年月日后可以手工算出星期几的,但我们现在的算法需要记住一个误差表才能进行计算,所以只能称为一种算法,还不是公式。 
        下面,我们试图消掉这个误差表…… 

        ============================= 
        消除闰年判断的条件表达式 
        ============================= 

        由于闰年在2月份产生的误差,影响的是后面的月份计算。如果2月是排在一年的最后的话,它就不能对其它月份的计算产生影响了。可能已经有人联想到了文章开头的公式中为什么1,2月转换为上年的13,14月计算了吧   :)

        就是这个思想了,我们也将1,2月当作上一年的13,14月来看待。 
        由此会产生两个问题需要解决: 
        1> 一年的第一天是3月1日了,我们要对   w   的计算公式重新推导 
        2> 误差表也发生了变化,需要得新计算 

        ①推导   w   计算式 
            1>   用前面的算法算出   0年3月1日是星期3 
                  前7天,   d   =   1---7     ===>     w   =   3----2 
                  得到   w   =   (d+2)   %   7 
                  此式同样适用于整个三月份 
            2>   扩展到每一年的三月份 
                  [d   +   2   +   y   +   (y-1)/4   -   (y-1)/100   +   (y-1)/400]   %   7 

        ②误差表 
================================================== 
月     误差   累计     模7 
3       3         0           0 
4       2         3           3 
5       3         5           5 
6       2         8           1 
7       3         10         3 
8       3         13         6 
9       2         16         2 
10     3         18         4 
11     2         21         0 
12     3         23         2 
13     3         26         5 
14     -         29         1 
================================================== 

        ③得到扩展到其它月的公式 
        e[]   =   {0,3,5,1,3,6,2,4,0,2,5,1} 
        w   =   [d+2   +   e[m-3]   +y+(y-1)/4-(y-1)/100+(y-1)/400]   %   7 
        (3   <=   m   <=   14) 

        我们还是将   y-1   的式子进行简化 
        w   =   [d+2   +   e[m-3]   +y+y/4-y/100+y/400]   %   7 
        (3   <=   m   <=   14) 

        这个式子如果当年是闰年,会告成多1的误差 
        但我们将1,2月变换到上一年的13,14月,年份要减1,所以这个误差会自动消除,所以得到下面的算法: 

        int   e[]   =   new   int[]{0,3,5,1,3,6,2,4,0,2,5,1}; 
        if(m   <   3)   { 
                m   +=   12; 
                --y; 
        } 
        int   w   =   (d+2   +   e[m-3]   +y+(y/4)-y/100+y/400)   %   7;   -----公式⑸ 

        我们可以看到公式⑸与公式⑷几乎是一样的,仅仅是误差天和一个常数的差别 
        常数的区别是由起始日期的星期不同引起的,0年1月1日星期日,0年3日1日星期三,有三天的差别,所以常数也从   -1   变成了   2。 

        现在,我们成功的消除了繁琐的闰年条件判断。 


        ============================= 
        消除误差表 
        ============================= 
        假如存在一种m到e的函数映射关系,使得 
                e[m-3]   =   f(m) 
        则我们就可以用   f(m)   取代公式⑸中的子项   e[m-3],也就消除了误差表。 

        由于误差表只有12个项,且每一项都可以加减   7n   进行调整,这个函数关系是可以拼凑出来的。但是这个过程可能是极其枯燥无味的,我现在不想自己去推导它,我要利用前人的成果。所谓前人栽树,后人乘凉嘛   :)

        文章开头开出的公式中的   2*m+3*(m+1)/5   这个子项引起了我的兴趣 

        经过多次试试验,我运行下面的代码 

        for(m=1;   m <=14;   ++m) 
                System.out.print((-1+2*m+3*(m+1)/5)%7   +   "   "); 
        System.out.println(); 

        天哪,输出结果与我的误差表不谋而合,成功了,哈哈 

        2   4   0   3   5   1   3   6   2   4   0   2   5   1 
        Press   any   key   to   continue... 

        上面就是输出结果,看它后面的12项,与我的误差表完全吻合!!! 

        现在就简单的,将   f(m)   =   -1   +   2*m   +   3*(m+1)/5   代入公式⑸,得到 

        w   =   (d+1+2*m+3*(m+1)/5+y+(y/4)-y/100+y/400)   %   7   ----公式6 
        约束条件:   m=1,m=2   时   m=m+12,y=y-1; 

        现在,我们得到了通用的计算星期的公式,并且“完全”是按自己的思想推导出来的(那个函数映射关系不算),只要理解了这个推导的步骤,即使有一天忘记了这个公式,也可以重新推导出来! 

        可能有人会注意到公式⑹与文章开头的公式相差一个常数   1,这是因为原公式使用数字0--6表示星期一到星期日,而我用0--6表示星期日到星期六。实际上是一样,你可以改成任意你喜欢的表示方法,只需改变这个常数就可以了。 


猜你喜欢

转载自blog.csdn.net/buknow/article/details/80324349