爱吱声
标题:
【半原创】计算某一天是星期几
[打印本页]
作者:
喜欢
时间:
2015-2-28 06:51
标题:
【半原创】计算某一天是星期几
本帖最后由 喜欢 于 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)
至此,圆满完成了一次……玩乐!
神牛同学请跟帖,俺要给你加分!谢谢你~~
作者:
holycow
时间:
2015-2-28 07:11
我后来又想到一个问题:
维特啊塞根德,为嘛修正值可以不随平闰年的变化而变化?
作者:
雨楼
时间:
2015-2-28 08:59
等夏天咱去草地里数蚂蚁吧?比这个好玩多了。要不弹玻璃球?
作者:
雨楼
时间:
2015-2-28 09:01
holycow 发表于 2015-2-27 18:11
我后来又想到一个问题:
学霸摔完肥鸡又要优化算法鸟。
作者:
喜欢
时间:
2015-2-28 09:19
本帖最后由 喜欢 于 2015-2-27 20:25 编辑
holycow 发表于 2015-2-27 18:11
我后来又想到一个问题:
哎,你提到的这个问题,它确实是一个问题!
当初我做那个m’的时候还想过,想着弄好一般的年份再考虑闰年,然后,就,忘,了!
刚才我去验算,艾玛,
用它的公式,凡是闰年的前两个月都算不对呀!!
用我的公式(以y-1代替y的算法)
呢?咳咳,
闰年的后十个月都算不对!
架不住我可以改哈,他的公式谁给他改呢?
把我的公式从这样:
=MOD(INT((E15-1)/4)+MOD((E15-1),7)+5*MOD(D15,4)+G15+VLOOKUP(F15,$A$14 : $C$25,3,FALSE),7)
改成:
=MOD(INT((E15-1)/4)+MOD((E15-1),7)+5*MOD(D15,4)+G15+VLOOKUP(F15,$A$14 : $C$25,3,FALSE)+IF(F15>=3,IF(MOD(E15,4)<>0,0,1)),7)
就能算对了!
修正内容:
若y是闰年,则从3月起的后十个月,都要再+1
运算结果:
C Y M D His His Mine Real
01 6 0 20 15 02 27 5 5 5 5
02 2 3 19 49 10 01 6 6 6 6
03 2 3 19 11 10 10 2 2 2 2
04 5 6 19 45 08 15 3 3 3 3
05 0 1 19 72 01 01 0 0 6 6
06 3 4 19 72 02 28 2 2 1 1
07 5 6 19 72 02 29 3 3 2 2
08 1 2 19 72 03 01 3 3 3 3
09 4 5 19 72 05 01 1 1 1 1
10 6 0 20 12 02 28 3 3 2 2
11 2 3 20 12 02 29 4 4 3 3
12 4 5 20 12 03 01 4 4 4 4
其中:
前3列是数据
中间4列是年月日
接下来两列是不同方式实现的网上那个公式,我叫它做His(他的)——在闰年的前两个月,都会算错!
倒数第2列是我(修正过)的公式算出来的——正确!
最后一列是查万年历查出来的实际“星期几”,作为正确值,做标尺用。
耶,这次真的圆满了!
神牛你再来,我还用分数谢谢你~
作者:
holycow
时间:
2015-2-28 09:21
握手,这个也是我最后得出来的公式
欢迎光临 爱吱声 (http://129.226.69.186/bbs/)
Powered by Discuz! X3.2