• 搞脑筋的日历积木


      今天无意中发现了一个很有奥妙的小玩具,它可以用来查询给定日期的礼拜。扭动年份到对应的月份下,就可以查看那个月具体某个日子是星期几。顿时觉得不可思议。

    少见多怪的我又开始瞎琢磨原理:于是召唤出了沉睡已久的蔡勒公式以及一系列它的变形。其他不想说,这个玩具表达的公式,看着最接近以下这一个:weekday=[C//42C+y+y//4+13(M+1)//5+d1]" role="presentation" style="position: relative;">weekday=[C//42C+y+y//4+13(M+1)//5+d1]

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

    而我更喜欢下面这个容易理解的公式:

    weekday=[(Y1)+(Y1)//4(Y1)//100+(Y1)//400+26(M+1)//10+d]" role="presentation" style="position: relative;">weekday=[(Y1)+(Y1)//4(Y1)//100+(Y1)//400+26(M+1)//10+d]

    Y是年份,M是月份,d是日数。1月和2月要按上一年的13月和14月来算,这时Y按上一年取值。
    算出来的数字再次除以7,余数是几就是星期几。比方,余数是0,则为星期日。

    这条公式就是个规律,而这个规律竟然是一个古代的聪明蛋提炼总结的。

    要推导出它,首先还是需要另一个日期作为定点已知它是星期几,这里隐晦的选择了公元前最后一天12-31是个星期日,作为锚。

    再接下来,如果用冗长但是容易理解的算法就是,计算离开那一天有多少个日子。然后再mod 7,余数就可以得到是星期几。

    (Y-1)*365+(Y-1)//4-(Y-1)//100+(Y-1)//400+(这一年出头的日子们)

    比如写作当日,2022年9月3日,则是

    (2022-1)*365 + (2022-1)//4- (2022-1)//100 + (2022-1)//400 + (31+28+31+30+31+30+31+31+3)=738401

    这货能被7除之后余数是6,所以是个星期六。

    我把上面的计算叫做逻辑本身的叙述;特点就是,计算过程就是在直白的说事情。接下来才是数学的变态,人家称呼这些个变形简化叫做数学之美。

    一定牢记,这个算法的根本就是找出7的余数是多少。那么为了优雅简化(数学的变态部分),这个说事儿的逻辑开始被找规律从而再次简化:

    (Y-1)*365 = (Y-1)*(364+1) #因为364=52*7

    进一步得:52*7*(Y-1) + (Y-1); 那么这里 其实我只要(Y-1)//7,因为前面是肯定能被7整除的,就直接扔掉了。

    剩下因为闰年,因为100年不是闰年,但是400年又是闰年,这一系列要累积天数的式子,中心思想都是数字7取整简化。

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

    再接下来,就到了要计算这一年出头的日子,我们开始看每个月的规律。首先记得这个月如果能被7整除,那么就是完整的礼拜周期。我们知道在30边上晃悠的且7可以整除的数字是28,那么就有,

    月份1月2月3月4月5月6月7月8月9月10月11月12月
    月余730或13232332323
    月余7的累积33或46或78或911或1213或1416或1719或2021或2224或2526或2729或30

    到了这里,可以看到3-12月,10个月连续稳定,一共26个日子。要分的更细,就会在3月到12月看到两个32323。一月和二月的余数放去尾巴那里,变为13月和14月,其实就是看成序号13和序号14。但是年份上就要算为是上一年序号的。这里会有这个企图的原因是2月的不确定性,如果可以把它当做最后一个月,就不需要去做分情况讨论平年和闰年,式子的未知变量会更少。

    3月4月5月6月7月8月9月10月11月12月13月(即下一年1月)14月(即下一年2月)
    32323323233余下什么都无所谓
    3581013161821232629

    请记住,因为这件事情本身就是在找从公元前最后一个日子至今的日子和7的整除关系,所以当把1,2月搬到一年的尾部,从而方便算累积超过28的日子是可以的。虽然看着怪抽象怪恶心,但本质就是会遵循遇7归0这个事儿。年份部分被7挤出来的日子,在上面已经都算好了。

    本年那些可以被7整除的部分都不在考虑后,就剩下被挤出的每个月底的多余和当月:

    accumulated = 13 * (M+1) // 5; (M\geq 3)

    这里可以理解成利用向下取整运算实现这个 13天累积出来对应5个连续月的循环。这个1月也就是13月好巧不巧,也是个3,也就是这串稳定的28有余的两段循环(32323 32323)的下一次循环开始那个位置,而没有破坏它。2月也就是14月又已经到了头。

    题外话,这个accumulated部分,我始终觉得有那么点得到答案推算式的意思。因为毕竟只要让M在[3,14]之间是说得过去的,也就可以了。毕竟这算是一个现实世界里面的局限算法而已,并不是个纯数学问题

    末了,之前那些年被7挤出的日子和今年里面被7挤出的日子又放一起,被7再除一下:weekday=[(Y1)+(Y1)//4(Y1)//100+(Y1)//400+26(M+1)//10+d]" role="presentation" style="position: relative;">weekday=[(Y1)+(Y1)//4(Y1)//100+(Y1)//400+26(M+1)//10+d]

    感叹一下,带perpetual calendar的机械手表买买可不便宜,但是它的底层逻辑居然可以靠一个几块钱的手工玩具来表达,是多么有意思啊。最上面的玩具图片,从它的盘面也能看出,它在不停的mod 7 mod 4。我是实在没本事花时间再把玩具本身的运作讲彻底了,遗憾。

    感受到了此刻,理解力与表达力两种能力上限。

    感谢此文引路:如何快速算出一个日期是星期几_ljx0305的博客-CSDN博客

  • 相关阅读:
    python网络通信之基础知识填坑
    C++-Cmake指令:file【文件操作命令】【比如:file GLOB命令主要用于匹配规则在指定的目录内匹配到所需要的文件】
    frp篇---frp-notify + Gotify 实现 FRP 用户上线通知
    .Net CLR GC 动态加载短暂堆阈值的计算及阈值超量的计算
    元器件正反(极性)检测案例
    B22-9-5
    如何在 Windows10 Professional 服务器上搭建自己的 Git 服务器。
    三次握手四次挥手
    【Python简明教程三十(完结)】字符串格式化
    linux003--Linux中文件查询,文件内容查询,文件查看命令的使用
  • 原文地址:https://blog.csdn.net/u011410413/article/details/126680139