设为首页收藏本站

爱吱声

 找回密码
 注册
搜索
查看: 1595|回复: 9
打印 上一主题 下一主题

[其他] 【半原创】计算某一天是星期几

[复制链接]
  • TA的每日心情
    奋斗
    昨天 00:00
  • 签到天数: 3137 天

    [LV.Master]无

    跳转到指定楼层
    楼主
    发表于 2015-2-28 06:51:37 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
    本帖最后由 喜欢 于 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)

    至此,圆满完成了一次……玩乐!

    神牛同学请跟帖,俺要给你加分!谢谢你~~

    评分

    参与人数 1爱元 +6 收起 理由
    holycow + 6 谢谢!有你,爱坛更精彩

    查看全部评分

  • TA的每日心情
    奋斗
    昨天 23:37
  • 签到天数: 2053 天

    [LV.Master]无

    沙发
    发表于 2015-2-28 07:11:58 | 只看该作者
    我后来又想到一个问题:

    维特啊塞根德,为嘛修正值可以不随平闰年的变化而变化?


    点评

    可是我没想明白前面6个字的……英文意思? -_-b  发表于 2015-2-28 09:30

    评分

    参与人数 1爱元 +8 收起 理由
    喜欢 + 8 这确实需要考虑一下!

    查看全部评分

  • TA的每日心情
    慵懒
    2020-1-3 00:51
  • 签到天数: 71 天

    [LV.6]出窍

    板凳
    发表于 2015-2-28 08:59:59 | 只看该作者
    等夏天咱去草地里数蚂蚁吧?比这个好玩多了。要不弹玻璃球?

    点评

    你是没挨过蚂蚁咬啊!问问东湖去~~:D  发表于 2015-2-28 09:28
  • TA的每日心情
    慵懒
    2020-1-3 00:51
  • 签到天数: 71 天

    [LV.6]出窍

    地板
    发表于 2015-2-28 09:01:22 | 只看该作者
    holycow 发表于 2015-2-27 18:11
    我后来又想到一个问题:

    学霸摔完肥鸡又要优化算法鸟。
  • TA的每日心情
    奋斗
    昨天 00:00
  • 签到天数: 3137 天

    [LV.Master]无

    5#
     楼主| 发表于 2015-2-28 09:19:48 | 只看该作者
    本帖最后由 喜欢 于 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列是我(修正过)的公式算出来的——正确!
    最后一列是查万年历查出来的实际“星期几”,作为正确值,做标尺用。

    耶,这次真的圆满了!

    神牛你再来,我还用分数谢谢你~

    点评

    ^_____________^  发表于 2015-2-28 09:35
    wait a second :p  发表于 2015-2-28 09:31

    评分

    参与人数 1爱元 +6 收起 理由
    holycow + 6

    查看全部评分

  • TA的每日心情
    奋斗
    昨天 23:37
  • 签到天数: 2053 天

    [LV.Master]无

    6#
    发表于 2015-2-28 09:21:32 | 只看该作者
    握手,这个也是我最后得出来的公式

    评分

    参与人数 1爱元 +8 收起 理由
    喜欢 + 8 你太快啦!我还没对齐呢~

    查看全部评分

    手机版|小黑屋|Archiver|网站错误报告|爱吱声   

    GMT+8, 2024-11-24 02:20 , Processed in 0.034684 second(s), 19 queries , Gzip On.

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

    快速回复 返回顶部 返回列表