原理: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.

0000-00-00
Total 0 Correct 0 Accuracy 0.0% Streak 0

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 推出最终星期

练熟之后,你看到一个日期时,不需要查日历,也能在几秒内算出它是星期几。