从「日星合一」到「末日算法」
前言
给定任意一个阳历日期,如何心算它是星期几?比如,2424年1月1日是星期几?
答案是星期一。因为2024年1月1日是星期一,而任意一天与400年后的同一天的星期数是相同的。(规律1)
有些读者可能会说,2424年1月1日这个日子太特殊了,换个普通的日子怎么办,比如12345年6月7日是星期几?
答案是星期四。我并没有使用计算机,而是使用「末日算法」来心算的,这个算法可以让你心算任意一个阳历日期是星期几。
直接介绍这个算法会有些枯燥,因此不妨先介绍我是如何了解到这个算法的。
日星合一
作为一名打工者,确认今天是星期几以及何时是星期六已经成为我的刚需。 大多数情况下,我会在手机或电脑查日历来解决这个需求,但有时我也会根据之前的日子推算出来。
后来我慢慢留意到一些特别容易推算星期几的日子,比如2023年5月1日,这一天刚好是星期一。 那么2023年5月的任意一天都特别容易计算对应的星期数——你只需要将日期除以7求余数即可。
为了表扬这些特殊的日子,我自作主张,给它们起了个名字:「日星合一」。(即日期数和星期数都是一)
那么多久会出现一次「日星合一」呢? 经过推算,我得出结论:每年至少会出现一次,至多会出现三次。(规律2)
比如,2023年只有一次「日星合一」,发生在5月1日;2024年则有三次「日星合一」,分别发生在1月1日、4月1日和7月1日。 后来我建了一个 Github 仓库, 来计算「日星合一」的日子,同时也想看看下自己还能发现什么规律。
后来我浏览维基百科词条 Determination of the day of the week,里面提供了多种计算任意日期是星期几的算法。 大部分算法看起来都有点复杂,并不适合心算,直到我看到一个词条:Doomsday rule (直译是「末日法则」,但更广为人知的名字是「末日算法」)。
末日算法
「末日算法」(Doomsday rule)是英国数学家康威(John Horton Conway)发现的,学过编程的朋友可能会对这个名字有点印象。 因为康威还设计过一个计算机程序「生命游戏」(Game of Lfe),我读大学时的《数据结构》的教科书里就提到过这个游戏。
末日
这个算法中,最有意思同时也最具有创造力的地方是提出「末日」(Doomsday)的概念。 康威发现每年都有一些比较容易记忆的日子,它们的星期数是相同的,比如每年的4月4日、6月6日、8月8日、10月10日和12月12日的星期数都是相同的。(规律3) 于是康威把这些日子称为「末日」。(所以这里的「末日」与我们平时常说的「世界末日」没有任何关系)
以上这五个日子特别好记,只需记住月份和日期是相同的大于2的偶数即可。 除了这五个日子之外,5月9日、9月5日、7月11日和11月7日也是「末日」。这四个日子可以用一句话来联想记忆:「我在 7-11 便利店上班,朝九晚五。」 此外,1月3日/4日(平年对应1月3日,闰年对应1月4日)和2月份最后一天(平年对应2月28日,闰年对应2月29日)也是「末日」。 3月份本来没有「末日」,但为了避免落单,把3月0日作为「末日」,其实就是2月份最后一天。
心算任意一天是星期几
就这样,我们每个月都找到了一个「末日」,它们的星期数是相同的。
只要我们知道某一年的「末日」是星期几,以「末日」为参考点,我们就能心算这一年任意一天是星期几了。
比如,2023年的「末日」是星期二,那么2023年12月25日是星期几呢?
心算过程如下:2023年的「末日」是星期二,那么2023年12月12日是星期二,2023年12月25日比2023年12月12日多13天,(2+13)%7=1
,因此是星期一。
非常简单,不是吗?
心算任意年份的「末日」
有些读者可能会问:我怎么知道任意一年的「末日」是星期几呢?
答案是:首先你需要记住整百的年份的「末日」,然后以它们为参考点,就可以心算任意年份的「末日」了。
由于400年一循环(任意年份与400年后的年份的「末日」必然相同),所以只需记住除以400的余数分别为0, 100,200 和 300 的年份的「末日」即可。 比如 1600年,1700年,1800年和1900年,它们的「末日」依次是星期二、星期日、星期五、星期三。
(这里也有个小规律:每增加100年,对应的「末日」的星期数增加5,所以其实只需要记住1600年的「末日」是星期二即可)
知道整百的年份的「末日」之后,我们就可以 心算任意一个年份的「末日」了(规律4):
- 确认年份对应的整百年份的「末日」,记为 C;
- 将年份除以 100,取余数(其实就是年份后两位),记为 Y;
- 将 Y 除以 4,取商,记为 L;
- 计算
(C+Y+L) % 7
,即为对应年份的「末日」的星期数。
以2023年为例,心算过程如下:
- 2023年对应的整百年份是2000,其「末日」是星期二,即 C=2;
- 2023年的后两位是23,即 Y=23;
- 23除以4的商是5,即 L=5;
- 计算
(2+23+5) % 7 =2
,因此2023年的「末日」是星期二。
证明
上面提到了4个规律:
- 规律1:任意一天与400年后的同一天的星期数是相同的。
- 规律2:「日星合一」现象每年至少会出现一次,至多会出现三次。
- 规律3:每年2月的最后一天、4月4日、5月9日、6月6日、7月11日、8月8日、9月5日、10月10日、11月7日和12月12日的星期数都是相同的。
- 规律4:心算任意年份的「末日」的算法。
接下来简单给出这4个规律的证明思路(当然,我鼓励有兴趣的读者可以先停下来,尝试自己证明):
证明规律1
- 任意400年期间,会有97个闰年和303个平年;
- 对于任意日期,每个平年的星期数会增加1(因为365除以7的余数是1);每个闰年的星期数会增加2(因为366除以7的余数是2);
- 由于
303 + 97 * 2 = 497
,而 497 刚好能被7整除,所以任意日期过了400年后星期数不变。
证明规律2
利用同余性质,很容易算出哪些月的第一天的星期数是相同的,不妨将第一天的星期数相同的月份分组。
平年时的分组如下:
- 第1组:1月、10月
- 第2组:2月、3月、11月
- 第3组:4月、7月
- 第4组:5月
- 第5组:6月
- 第6组:8月
- 第7组:9月、12月
闰年时的分组如下:
- 第1组:1月、4月、7月
- 第2组:2月、8月
- 第3组:3月、11月
- 第4组:5月
- 第5组:6月
- 第6组:9月、12月
- 第7组:10月
可见,无论是平年或闰年,都刚好有7个组,由于不同组的星期数不同,这意味着必有1组是星期一; 又由于每组的数量只有1、2或3这三种情况,因此月份第1天是星期一的情况至少1个,至多3个。
证明规律3
每年2月的最后一天、4月4日、5月9日、6月6日、7月11日、8月8日、9月5日、10月10日、11月7日和12月12日,这些日期中, 下一个日期与上一个日期的间隔分别是35、35、28、35、28、28、35、28 和 35,都是7的倍数,因此它们的星期数都相同。
证明规律4
这个算法的核心是计算任意年份与对应的整百年份的「末日」的偏移量。
根据规律1,平年的偏移量为1,闰年的偏移量为2;而 Y+L
便是平年和闰年对应的偏移量的总和。
后记
本文讨论的所有规律或算法,其实只需要小学时的「同余」知识就能证明。 这就是数学的一大魅力——一个简单的定理却可以推导出很多有趣的规律。
最后,也许有人会问:「研究这些东西有什么用呢?不会显得你很无聊吗?」 我曾经也这样自问,直到我在维基百科词条Determination of the day of the week 看到高斯也提出了一个计算某天是星期几的算法,我终于释怀: 我何德何能,又何等荣幸,能跟有史以来最伟大的数学家之一、被誉为「数学王子」的高斯研究过同一个问题!