TA的每日心情 | 奋斗 4 小时前 |
---|
签到天数: 3131 天 [LV.Master]无
|
本帖最后由 喜欢 于 2015-2-27 20:31 编辑
前天(25号)去玩了一个题:用公式计算某一天是星期几,写在日志里。看了我的日志,
有人引介我去看他们讨论过的做这件事的帖子;
有人说费那个劲干嘛?无论是Excel还是其它编程语言,都有现成的功能可以直接调用;
不过更有人帮我解答了我的疑问!——这就是神一般的牛同学—— @holycow !
今天得空,我把这个题重新审视了一遍,把神牛的解释也就是原公式读懂了,并学习了一下前天尚不大会用的Excel的数组功能,改进了我的公式。现在我可以用三个不同的方式实现这个计算功能,算出历史上、现在和将来任何一天是星期几!
问我为什么要算这个?就好比问我为什么要玩、为什么要上网、为什么要出游、为什么要做开心的事一样,我喜欢! 在“软件人家”那个帖子中,大家纷纷表示做过这个题,用各种语言。那就是了。当年我也做过这个题。编程序么,这是一个很不错的题,可以用到各种语句、功能……最后要打印出来“月历”。我还给自己加码,专门用循环语句实现打印。有同学不解,问我为什么要舍简求繁?操练呀,做这题本就为了操练各种语句和功能嘛,简单的途经练不到循环语句呀。目的又不是打印月历,谁还缺挂历不成?哦,忘了说,当时我用的是汇编语言。
扯远了,拉回来。我不管它是儒略历还是格里高利历,反正就是现在我们用的这个公历,按照这个公式往前往后都能算,算出任何一天是星期几。(如果说公元1582年10月4日或10月15日之前的日子照这算法有所不同,那不是这公式的错,那是历史的错!需要历史学家去修正,咱不管那一段儿~ )
这公式是网上找来的,如下:
公式:
W=[y/4]+r(y/7)-2r(c/4)+m'+d
此处得到的W要对7取余,0表示星期天;
其中y为年份的后两位;c为年份的前两位;d为日期;[] 为取整的意思;r为取余的意思(在公式中分别对7、对4取余);
m’为“月份的修正数”,即对应每个月都有一个修正数,从1月到12月依次为:
6,2,2,5,0,3,5,1,4,6,2,4
我前天只用Excel实现了这个算法,但对公式还不是很理解。于是神牛同学帮我解析了这个公式:
我想出来了。
每年正常365天,除7余1,所以造成的星期位移是1;闰年是2.
每400年的位移是400+97闰 = 497 mod 7 = 0,正好一循环。而每一百年的位移是100 + 24 = 124 mod 7 = 5. 所以r(c/4)那项之前的因子应该是5,mod 7运算+5 = -2, 故取 -2r(c/4)
r(y/7) + [y/4]是最近100年的位移。
这个位移实际已经算到ccyy+1年,所以一月一号要倒回来365天,应该-1,结果他取修正+6. 其余各个月的修正迎刃而解。
我读懂了神牛同学的分析,并用自己的语言复述它如下,请神牛同学认可:
● 首先,公元1年1月1日是星期一,这个是基础;
● 其次,所有7的倍数天数就不必算了,因为星期一到星期天是个循环,周而复始;
● 我们只要算自“公元1年1月1日”以来所有7的倍数之外的那些天加起来,对7取余,就知道是星期几;
● 每年正常365天,除7余1,所以要算的就是每年1天;闰年再加1天;
● 每400年的位移就是400天+97(闰)天 = 497天;497 mod 7 = 0,正好一循环。所以不用算这部分;
● 不足400年的百年部分c要算:每一百年的位移是100天+24(闰)天=124天,124 mod 7 = 5。有一个百年就是一个5,r(c/4)就是1、2或3个百年(逢4为零,不必算)。那项之前的因子应该是5,mod 7运算+5 = -2, 故取 -2r(c/4);
● 然后算不足百年的年数y,只取mod7即可:r(y/7);
● 再算此间闰年有过多少个:[y/4];
● 再加上此前经过的各个整月累计余下了多少个少于7的日子,即m’;
● 再加上本月至此的天数d;
● 以上的总和对7取余,得几就是星期几,0为星期天。
● 最后还有一点要单说,就是那m’是如何确定的。我要跟神牛同学握握手——因为你也没放过这个细节(当然不能放过^^)。
我算出来的m’分明是:
0,3,3,6,1,4,6,2,5,0,3,5
但公式中给出的却是:
6,2,2,5,0,3,5,1,4,6,2,4
整差1
这是因为公式中使用了y——今年的年份,本来应该用(y-1)才对。于是公式就在m’处做了修正。
● 最后一点:如果将来“逢百闰逢4百不闰”又有误差了,比如逢4千又闰了,那是将来的事,此处不能预估之。
好了。现在我已算出一些日子的结果,列出如下(为对齐起见,适当加了前置的0):
01 6 0 C Y M D Day Day Day
02 2 3 20 15 02 25 3 3 3
03 2 3 20 15 02 26 4 4 4
04 5 6 20 15 02 27 5 5 5
05 0 1 20 15 02 28 6 6 6
06 3 4 20 15 03 01 0 0 0
07 5 6 20 15 03 02 1 1 1
08 1 2 20 15 03 03 2 2 2
09 4 5 19 49 10 01 6 6 6
10 6 0 19 11 10 10 2 2 2
11 2 3 19 45 08 15 3 3 3
12 4 5 20 11 07 01 5 5 5
后面三列我用了不同的公式,达到了相同的结果:
1,前天对数组算法不熟悉,索性试试嵌套的IF语句我能否写得明白。经过一番周折,写明白了:
=MOD(INT(E15/4)+MOD(E15,7)-2*MOD(D15,4)+G15+IF(F15<>5,IF(F15<>8,IF(AND(AND(F15<>2,F15<>3),F15<>11),IF(F15<>6,IF(AND(F15<>9,F15<>12),IF(AND(F15<>4,F15<>7),IF(AND(F15<>1,F15<>10),"wrong",6),5),4),3),2),1),0),7)
很不简单哪!那么多括号嵌来嵌去的,不晕才怪!
2,今天学习了一下,用数组功能实现原公式,很简单明了(即,用到上图表中的前面两列数据):
=MOD(INT(E15/4)+MOD(E15,7)-2*MOD(D15,4)+G15+VLOOKUP(F15,$A$14: $B$25,2,FALSE),7)
3,既然原公式并不是很直观,那我索性就把它改一下,用更直观的方式(用到上图表中的第一和第三列数据):
=MOD(INT((E15-1)/4)+MOD((E15-1),7)+5*MOD(D15,4)+G15+VLOOKUP(F15,$A$14: $C$25,3,FALSE),7)
至此,圆满完成了一次……玩乐!
神牛同学请跟帖,俺要给你加分!谢谢你~~
|
评分
-
查看全部评分
|