Conway's Doomsday
原理:Conway's Doomsday Algorithm
这个小游戏基于数学家 John H. Conway 提出的 Doomsday Algorithm,中文常译为“末日算法”。它的目标是:给定一个公历日期,快速心算出它是星期几。
这里的 “Doomsday” 不是灾难意义上的“末日”,而是指某一年中一组特殊日期共同落在的那个星期几。这个星期几叫这一年的 Doomsday。
本游戏采用下面的星期编号:
0 = Sunday
1 = Monday
2 = Tuesday
3 = Wednesday
4 = Thursday
5 = Friday
6 = Saturday
Practice
Doomsday Trainer
Practice weekday calculation with the algorithm.
1. 为什么可以用 mod 7 计算星期?
星期每 7 天循环一次,所以日期和星期的关系本质上是一个 mod 7 问题。
如果某一天是 Saturday,编号为 6,那么 6 天之后就是:
6 + 6 = 12
12 mod 7 = 5
5 = Friday
所以只要知道某个已知日期的星期,就可以通过日期差推出另一个日期的星期。
Doomsday Algorithm 的核心就是为每一年找出一批容易记忆的“已知日期”。
2. 什么是 Doomsday?
在同一年中,下面这些日期总是落在同一个星期几:
3/14
4/4
6/6
8/8
10/10
12/12
5/9
9/5
7/11
11/7
这些日期共同落在的星期几,就叫这一年的 Doomsday。
例如,如果某一年的 Doomsday 是 Saturday,那么这一年里:
3/14 是 Saturday
4/4 是 Saturday
6/6 是 Saturday
8/8 是 Saturday
10/10 是 Saturday
12/12 是 Saturday
5/9 是 Saturday
9/5 是 Saturday
7/11 是 Saturday
11/7 是 Saturday
一月和二月稍微特殊,因为闰年会多出 2 月 29 日:
普通年:
1/3, 2/28
闰年:
1/4, 2/29
3. 为什么这些日期会落在同一个星期几?
这是因为它们之间相差的天数都是 7 的倍数。
例如:
4/4 到 6/6 相差 63 天
63 = 9 × 7
所以 4/4 和 6/6 一定是同一个星期几。
同理,6/6、8/8、10/10、12/12 之间的差距也都是 7 的倍数。奇数月份的日期则可以用口诀记忆:
I work from 9 to 5 at the 7-Eleven.
对应:
9/5, 5/9, 7/11, 11/7
这些日期不是随便选出来的,而是 Conway 选择的一组容易记忆、并且彼此相差 7 的倍数的日期。
4. 问题转化:只需要算出这一年的 Doomsday
如果我们知道 2026 年的 Doomsday 是 Saturday,那么计算 2026-06-12 就很简单。
因为六月的 Doomsday 日期是:
6/6
所以:
2026-06-06 是 Saturday
2026-06-12 比 6/6 晚 6 天
Saturday + 6 = Friday
因此:
2026-06-12 = Friday
所以整个算法真正要解决的问题是:
怎样快速算出某一年的 Doomsday?
5. 年份计算的结构
公历中,普通年有 365 天:
365 = 52 × 7 + 1
所以普通年会让下一年的同一日期星期数往后移动 1 天。
闰年有 366 天:
366 = 52 × 7 + 2
所以闰年会让下一年的同一日期星期数往后移动 2 天。
因此,在同一个世纪内部,从世纪初到某一年,Doomsday 的变化主要取决于:
经过了多少个年份
其中有多少个闰年
如果年份后两位是 y,那么从世纪初到这一年,大致贡献是:
y + floor(y / 4)
因为每经过一年,星期往后移动 1;每遇到一个闰年,再额外多移动 1。
不过 Conway 为了方便心算,把这个式子改写成了更适合口算的形式。
令:
q = floor(y / 12)
r = y mod 12
s = floor(r / 4)
则:
year offset = q + r + s
这个结果与:
y + floor(y / 4)
在 mod 7 意义下是一样的,但 ÷12 的版本更适合心算。
原因是:
y = 12q + r
floor(y/4) = 3q + floor(r/4)
所以:
y + floor(y/4)
= 12q + r + 3q + floor(r/4)
= 15q + r + floor(r/4)
而:
15q ≡ q (mod 7)
因为:
15 ≡ 1 (mod 7)
因此:
y + floor(y/4) ≡ q + r + floor(r/4) (mod 7)
这就是 Conway 年份心算法的来源。
6. 什么是 century anchor day?
现在还差一个起点。
如果只知道年份后两位 y,还不够。比如:
1926
2026
2126
它们的后两位都是 26,但它们属于不同世纪,所以起点不同。
每个世纪都有一个基准星期,叫 century anchor day。它表示该世纪起始年份的 Doomsday。例如 2000s 的 anchor day 是 2000 年的 Doomsday。
例如:
1900s → Wednesday = 3
2000s → Tuesday = 2
2100s → Sunday = 0
2200s → Friday = 5
也就是说:
1900 年的 Doomsday 是 Wednesday
2000 年的 Doomsday 是 Tuesday
2100 年的 Doomsday 是 Sunday
2200 年的 Doomsday 是 Friday
这个基准星期就是该世纪计算所有年份的起点。
7. century anchor day 为什么会循环?
公历的闰年规则是:
普通年份:能被 4 整除的是闰年
世纪年份:必须能被 400 整除才是闰年
所以:
1600 是闰年
1700 不是闰年
1800 不是闰年
1900 不是闰年
2000 是闰年
公历每 400 年完整循环一次。400 年的总天数是:
400 × 365 + 97 = 146097
而:
146097 = 20871 × 7
也就是说,400 年的总天数正好是 7 的倍数。
因此,公历中的星期模式每 400 年重复一次。century anchor day 也每 400 年重复一次。
这就是为什么:
1600s 和 2000s 的 century anchor day 相同
1700s 和 2100s 的 century anchor day 相同
1800s 和 2200s 的 century anchor day 相同
1900s 和 2300s 的 century anchor day 相同
常用表如下:
1600s → Tuesday = 2
1700s → Sunday = 0
1800s → Friday = 5
1900s → Wednesday = 3
2000s → Tuesday = 2
2100s → Sunday = 0
2200s → Friday = 5
2300s → Wednesday = 3
在 Gregorian calendar 且使用 Sunday = 0, Monday = 1, ..., Saturday = 6 的编号下,可以使用下面的公式。
设:
century = floor(year / 100)
则:
century anchor = (5 × (century mod 4) + 2) mod 7
例如 2026 年:
century = 20
20 mod 4 = 0
century anchor = (5 × 0 + 2) mod 7
= 2
= Tuesday
所以 2000s 的 century anchor day 是 Tuesday。
8. 完整算法
给定一个日期:
year-month-day
第一步,算 century anchor day:
century = floor(year / 100)
century anchor = (5 × (century mod 4) + 2) mod 7
第二步,取年份后两位:
y = year mod 100
第三步,算年份偏移:
q = floor(y / 12)
r = y mod 12
s = floor(r / 4)
year offset = q + r + s
第四步,算该年的 Doomsday:
year doomsday = century anchor + year offset (mod 7)
第五步,找到该月份的 Doomsday 日期。
普通年:
Jan 3
Feb 28
Mar 14
Apr 4
May 9
Jun 6
Jul 11
Aug 8
Sep 5
Oct 10
Nov 7
Dec 12
闰年:
Jan 4
Feb 29
其他月份不变
第六步,计算目标日期和该月份 Doomsday 日期的差:
offset = target day - month doomsday day
最后:
final weekday = year doomsday + offset (mod 7)
9. 例子:计算 2026-06-12
先算 century anchor day。
2026 属于 2000s:
century = 20
20 mod 4 = 0
century anchor = (5 × 0 + 2) mod 7
= 2
= Tuesday
再算年份后两位:
y = 26
使用 Conway 的年份心算法:
26 = 2 × 12 + 2
q = 2
r = 2
s = floor(2 / 4) = 0
year offset = 2 + 2 + 0 = 4
所以 2026 年的 Doomsday 是:
Tuesday + 4
= 2 + 4
= 6
= Saturday
六月的 Doomsday 日期是:
6/6
所以:
2026-06-06 是 Saturday
2026-06-12 比 6/6 晚 6 天
因此:
Saturday + 6
= 6 + 6
= 12
12 mod 7 = 5
5 = Friday
最终:
2026-06-12 = Friday = 5
10. 这个游戏训练的是什么?
这个游戏不是让你死记每个日期的答案,而是训练你快速完成四个步骤:
1. 找 century anchor day
2. 算 year doomsday
3. 找 month doomsday date
4. 用 mod 7 推出最终星期
练熟之后,你看到一个日期时,不需要查日历,也能在几秒内算出它是星期几。