星期几怎么算——牛逼公式

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]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值