不查日历怎么知道任何一天是星期几

作者:刘夙  2004-5-2 1:53:20 
博客中国(Blogchina.com)  
 
文/葛民勤

---------------------------------------------------------------------------
摘要:

  最常见的公式:

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

Y是年份数,D是这一天在这一年中的累积天数,也就是这一天在这一年中是第几天。

  最好用的是蔡勒公式:

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

C是世纪数减一,y是年份后两位,M是月份,d是日数。1月和2月要按上一年的13月和
14月来算,这时C和y均按上一年取值。

  两个公式中的[...]均指只取计算结果的整数部分。算出来的W除以7,余数是几就
是星期几。如果余数是0,则为星期日。
---------------------------------------------------------------------------

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

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

  答案是肯定的。其实我们也常常在这样做。我们先举一个简单的例子。比如,知道了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)

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

2004年5月2日初稿

【声明】博客中国刊载此文不代表同意其说法或描述,仅为提供更多信息,也不构成任何投资建议。转载请注明出处。 
【本文网址】http://www.blogchina.com/new/display/30137.html 
 现有评论 16条  关注指数:4478  打印模式    
 
fsc518 于 2004-8-1 21:51:29写道:
    已知:2004年M月D日是星期N,问10后、100年后、1000年后、10000年后的M月D日是星期几?

  上面的公式(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)

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

2004年5月2日初稿

【声明】博客中国刊载此文不代表同意其说法或描述,仅为提供更多信息,也不构成任何投资建议。转载请注明出处。 
【本文网址】 http://www.blogchina.com/new/display/30137.html  
 现有评论 16条  关注指数:4478  打印模式    
 
fsc518 于 2004-8-1 21:51:29写道:
    已知:2004年M月D日是星期N,问10后、100年后、1000年后、10000年后的M月D日是星期几?

  上面的公式(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)

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

2004年5月2日初稿

【声明】博客中国刊载此文不代表同意其说法或描述,仅为提供更多信息,也不构成任何投资建议。转载请注明出处。 
【本文网址】 http://www.blogchina.com/new/display/30137.html  
 现有评论 16条  关注指数:4478  打印模式    
 
fsc518 于 2004-8-1 21:51:29写道:
    已知:2004年M月D日是星期N,问10后、100年后、1000年后、10000年后的M月D日是星期几?

IP: 61.145.128.*
引用并回复此留言 
 
fsc518 于 2004-8-1 21:51:20写道:
    已知:2004年M月D日是星期N,问10后、100年后、1000年后、10000年后的M月D日是星期几?

IP: 61.145.128.*
引用并回复此留言 
 
fsc518 于 2004-7-28 16:27:17写道:
    祝葛先生生日快乐!望葛先生富有创意的、脍炙人口的作出不断涌现出来,以启迪广大科普爱好者的思维、拓展广大科普爱好者的视野。

IP: 218.18.37.*
引用并回复此留言 
 
fsc518 于 2004-7-21 19:52:32写道:
    以葛先生的生日1982年7月29日为例,采用笔者给出的公式:
     w=[y/4]+ r(y/7)-2r(c/4)+m’+d
     =[82/4] + r(82/7)-2r(19/4)+7’+29
     =20+5-2*3+5+29=53 (除以7余4)
     即诸葛先生出生的那一天是星期四。
     W(2004,7,29)=39(除以7余4)
     即诸葛先生今年的生日也是星期四.同理,诸葛先生明年的生日则是星期五。
     需要指出的是,用笔者给出的公式,只需稍加训练 ,即可用心算——而且在大多数情况下几秒钟就能搞定(而用蔡勒公式进行心算是非常困难的)。
     欢迎批评指正。Email:fsc518@126.com。
    
     冯思琮2004年7月21日

IP: 218.18.150.*
引用并回复此留言 
 
fsc518 于 2004-7-19 9:22:25写道:
    若采用本人给出的公式:
     w=[y/4]+r (y/7)-2r(c/4)+m’+d
     = [97/4]+r (97/7)-2r(19/4)+7’+1
     =24+6-2*3+5+1
     =30 (除以7余2)
     即1997年7月1日是星期二。
     请登陆www.tom.com、中国科普博览、www.xilu.com之科普论坛阅读本人的文章“自己就是一本活日历--对蔡勒(Zeller)公式的改进”及“n阶(n为大于等于7的素数)立体幻方的一种独特构造方法”。请联系本人,Email:fsc518@126.com.
     【在aulian 的大作中提到:】好文!
     以前我也研究过这个问题,不过还不如那个“最常见的公式”好用呢
     献个丑吧
    
     以1997
★★★
IP: 61.145.129.*
引用并回复此留言 
 
fsc518 于 2004-7-18 21:58:55写道:
    请通过www.google.com搜索“科普论坛”选择www.tom.com或中国科普博览之科普论坛阅读本人的文章“自己就是一本活日历---对蔡勒(Zeler)公式的改进”。Email:fsc518@126.com,欢迎联系。

IP: 218.18.151.*
引用并回复此留言 
 
aulian 于 2004-5-9 12:16:28写道:
    好文!
     以前我也研究过这个问题,不过还不如那个“最常见的公式”好用呢
     献个丑吧
    
     以1997年7月1日为例
     (1)取97。
     (2)用第(1)步的值除以4,仅保留结果的整数部分留用,不考虑余数。
     97/4=24…1
     (3)查月份表,找到7月对应的数是0。
     (4)把日子与前三步的数加数,如1+97+24+0=122。
     (5)用第(4)步算得的值除以7,保留余数留用。
     如122/7=17…3
     (6)查年份表,把对应的数加上第五步的余数
     如0+3=3
     (7)查星期表,即对应星期
    
     附:月份表
     1月 1(闰 0)
     2月 4(闰 3)
     3月 4
     4月 0
     5月 2
     6月 5
     7月 0
     8月 3
     9月 6
     10月 1
     11月 4
     12月 6
    
     年份表
     1900-1999 0
     2000-2099 -1
    
     星期表
     1 星期日
     2 星期一
     3 星期二
     4 星期三
     5 星期四
     6 星期五
     7 星期六
     0 星期六
★★★★
IP: 218.12.209.*
引用并回复此留言 
 
chinablogger 于 2004-5-5 14:45:11写道:
    我同意dumbo
     是的]
     中国人就喜欢搞这一套!
     何时也来个改革?期盼!

IP: 210.29.158.*
引用并回复此留言 
 
胡喊三 于 2004-5-5 10:52:18写道:
    不错,
     我觉得一个公式这样推理下来让人很容易接受,
     如果我们大学里的数学课程能够这样的话就好了.
     其实中国的教育缺的就是这个,
     试着找本书看看就知道.
     这样的教程是自学的好帮手!
     鼓励....
    
★★★★
IP: 218.75.208.*
引用并回复此留言 
 
newguyer 于 2004-5-4 19:19:20写道:
    趣味数学
     可惜博客中国的人大多数过于浮躁,没有可能静下心来学习这些东西
     原来以为博客中国上面什么都谈,后来才发现,他们只喜欢说IT,然后又万分小心的涉及政治,永远没有可能是综合的。。。。。。。

IP: 218.75.131.*
引用并回复此留言 
 
www.6pm.com 于 2004-5-4 18:58:36写道:
    其实有一个更简单的办法。看了这篇文章后我放心了。

IP: 218.79.20.*
引用并回复此留言 
 
dumbo 于 2004-5-4 18:17:55写道:
    这个帖子恐怕是贴错地方了,放在CSDN上支持的人会多。博客中国上真懂技术的没有几个人,所以大多数人不知道这个公式有什么意义。联想到前一段时间的WAPI争论,发现中国人一见到什么事,不是先想技术,而是先想政治。并且不懂的人发言,懂的人沉默,可谓中国特色。

IP: 61.48.32.*
引用并回复此留言 
 
胜昌镐 于 2004-5-3 23:25:02写道:
    何必故作高深,搞得那么复杂?看看日历不就得了。
★★
IP: 211.141.95.*
引用并回复此留言 
 
lumg 于 2004-5-2 18:36:44写道:
    没有任何实用价值!也许向别人吹吹牛还可以

IP: 211.67.30.*
引用并回复此留言 
 
zm118 于 2004-5-2 9:22:23写道:
    有日历干吗不查?

IP: 220.187.128.*
引用并回复此留言 
 
Janada 于 2004-5-2 9:02:57写道:
    还不如查日历简单!不用Google怎么知道你要的网页在哪里?

IP: 61.49.99.*
引用并回复此留言

不查日历怎么知道任何一天是星期几相关推荐

  1. 【Pyecharts50例】Calendar/日历图显示中文标签/月份显示中文/星期显示中文/name_map

    前言 在日历中日期标签默认显示的都是英文,如下 通过如下代码可将标签设置为英文: # 星期显示中文 daylabel_opts=opts.CalendarDayLabelOpts(name_map=' ...

  2. 日历每月一号对应相应的星期

    选一天作为基准,硬编码到程序中,用你要计算的日期减去基准日,得出其中的天数,模7取余数,再进行计算. 比如2010.01.01是星期五.现计算2012.12.31日: 2012.12.31-2010. ...

  3. arctanx麦克劳林公式推导过程_蔡勒(Zeller)公式及其推导:快速将任意日期转换为星期数...

    0. 本文的初衷及蔡勒公式的用处 前一段时间,我在准备北邮计算机考研复试的时候,做了几道与日期计算相关的题目,在这个过程中我接触到了蔡勒公式.先简单的介绍一下蔡勒公式是干什么用的. 我们有时候会遇到这 ...

  4. 日历生成算法-中国公历

    日历在我们的生活中扮演着十分重要的角色,上班.上学.约会都离不开日历.每年新年开始,人们都要更换新的日历,你想知道未来一年的这么多天是怎么被确定下来的吗?为什么去年的国庆节是星期五而今年的国庆节是星期 ...

  5. [转]日历生成算法-中国公历(格里历)

    转自吹泡泡的小猫,原文地址:http://blog.csdn.net/orbit/article/details/7749723 日历在我们的生活中扮演着十分重要的角色,上班.上学.约会都离不开日历. ...

  6. 算法系列之十七:日历生成算法-中国公历(格里历)(上)

    日历在我们的生活中扮演着十分重要的角色,上班.上学.约会都离不开日历.每年新年开始,人们都要更换新的日历,你想知道未来一年的这么多天是怎么被确定下来的吗?为什么去年的国庆节是星期五而今年的国庆节是星期 ...

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

    /*********************************************************************************** 引用的网址: 基姆拉尔森公式推 ...

  8. 代码:显示查询的日历

    package myjavase; import java.awt.*; import java.awt.event.*; import java.util.Calendar; import java ...

  9. kde菜单图标显示不全_大小仅 1M!在电脑菜单栏上自定义日历,规划时间更方便...

    之前我从 Windows 平台转到 Mac 平台的时候,有一点使用不习惯的就是日历部分. Windows 平台上,在右下角点击时间就会显示出当前月份的日历,还会有星期的显示,此时我就可以根据日历规划日 ...

最新文章

  1. 数据结构之【线性表】(顺序表、链表的基本操作实现)
  2. python就业方向及工资-Python的就业方向有哪些?
  3. 地图与定位之大头针视图
  4. 解决fragment replace 重叠现象
  5. java ee maven_针对新手的Java EE7和Maven项目–第8部分
  6. python 实现 CRC 冗余码的方法
  7. 第三十八篇、给UITabBar按钮的动画效果
  8. 计算机软件考试初级什么好考,初级程序员好考吗
  9. python刷新网页_python调用浏览器刷新网页的简单示例
  10. 桥接、交换机、路由器、网桥、网关
  11. php app 银联支付,php银联网页支付实现方法
  12. 安卓6.0系统一键激活XPOSED框架的方法
  13. layui日期插件样式冲突
  14. [LOJ6515]「雅礼集训 2018 Day10」贪玩蓝月
  15. 半导体|台积电今年上半年营收达1016亿元 同比减少4.5%
  16. 用c++实现贪吃蛇小游戏,初学者记录一下首次实现的经历,有超详细的思路与语法讲解,新手向
  17. 【git】统计每个人的代码行数
  18. iOS 的用户认证:使用Swift和Ruby on Rail
  19. java中set的特点_java -- Set 用法及特点
  20. 中职计算机专业介绍,【中职计算机专业介绍】中职计算机专业教学改革探幽

热门文章

  1. 华为平板鸿蒙发布,华为新款平板与P50一同发布!有望搭载鸿蒙系统
  2. python 打印三角形
  3. HBuilder真机联调iPhone手机设备问题+未受信任企业处理方法
  4. 使用Win10 Hyper-V 创建虚拟机
  5. R语言与LASSO回归和LAR算法
  6. VHDL实现USART
  7. 【整理】Python中的re.search和re.findall之间的区别和联系 + re.finall中带命名的组,不带命名的组,非捕获的组,没有分组四种类型之间的区别
  8. 某企业发放的奖金根据利润进行提成,利润低于或等于10万元时,奖金为利润的10%;利润高于10万元低于20万元时,低于10万元部分,奖金按利润的10%提成。。。。。。
  9. 数据迁移——技术选型
  10. Windows CMD 检擦电脑是否被入侵[简单办法]