机械万年历的数学原理
话题:#数学# #万年历#

逢年过节,人们难免会送一些小礼物,这里面就有一种机械万年历,

虽然说是万年历,但实际上只有几十年,可即便如此,也是设计的非常精妙。
机械万年历是月历。传统的月历,是一张以星期为表头的表格,日期被填入的 5×7 格子中,

这里,不同月份之间,表头是固定的,变化的是日期位置。日期位置的变化不利于机械实现,于是,我们让 日期位置不变,反过来,调整表头中的星期,得到,

观察发现:
若 以大月31天为准(多出来的做冗余量),则 不同月份的日期是同一个4×7格子;
若 将表头中星期向两边无限延伸,则 不同月份 得到的是同一个无线列;

这样一来,不同月份之间,只是 日期格子 与 星期列 之间的 相对位置 不同而已,接下来需要做的是确定这个 “定位”,也就是 确定 每月1日 是星期几。
设 A 年的 第一天 是 星期 a,即,
一月1日 是 星期 a;
然后,日期 每 增加7天,星期 又回到 a,也就是 a + 7 ≡ a (mod 7),而 一月是 31天,于是 可计算出,
二月1日 是 星期 a + (31 mod 7) = a+3 (mod 7);
接着,可依次算出,
三月1日 是 星期 a+3 + (28 mod 7) = a+3 (mod 7);
四月1日 是 星期 a+3 + (31 mod 7) = a+6 (mod 7);
五月1日 是 星期 a+6 + (30 mod 7) = a+8 ≡ a+1 (mod 7);
六月1日 是 星期 a+1 + (31 mod 7) = a+4 (mod 7);
七月1日 是 星期 a+4 + (30 mod 7) = a+6 (mod 7);
八月1日 是 星期 a+6 + (31 mod 7) = a+9 ≡ a+2 (mod 7);
九月1日 是 星期 a+2 + (31 mod 7) = a+5 (mod 7);
十月1日 是 星期 a+5 + (30 mod 7) = a+7 ≡ a (mod 7);
十一月1日 是 星期 a + (31 mod 7) = a+3 (mod 7);
十二月1日 是 星期 a+3 + (30 mod 7) = a+5 (mod 7);
这样就得到了一张表:

这里需要注意,以上 考虑的是 平年的情况,若 A 是闰年,则 二月 会多1天,这导致 上表 三到十二月 都要向后移动一格,这很不划算,于是,可以考虑 保持 三到十二月 不变 让 一二月 向前 移动 一格,即,
闰年一月1日 是 星期 a - 1 ≡ a + 6 (mod 7);
闰年二月1日 是 星期 a+3 - 1 = a + 2 (mod 7);
因为,只有两个月发生改变,没必要新做一张新表, 可以用记号标出 加入上表中,于是得到:

至此,我们就确定了任何一年内,每个月1日的星期定位,月份表内各月的定位固定,不同的是月份表在年份列上的定位。

接下来,我们需要确定每一年第1天的星期,
若 A 是平年,则有 365天,由 356 mod 7 = 1,知道 A+1年的 第1天 是 星期 a+1;
若 A 是润年,则多 1天,因此 A+1年的 第1天 是 星期 a+2;
于是我们得到:

通过 这个 表,我们可以 将 定位 年份列 的 月份表 就和 定位 星期列 的 日期表 关联起来,以 2023年 为准,有:

我们发现 月份表每月关于 年的定位 和 对应月 日期表关于 星期列 的定位 刚好相反,于是可以考虑 中心对称 圆盘 将 月份表 和 日期表 整合在一起,得到:

另外,无限星期列 可以 通过 圆环 来实现,然后 圆环和圆盘 同圆心,以方便定位。为了让星期首位相连,圆环分割数必须是 7 的倍数,为了能 能容纳 下 月份表 和 日期表,圆盘 分割数必须大于 7 + 7 = 14,因为要对齐定位,圆环 和 圆盘 分割数 必须相同,这里选择 4×7 = 28 的分割方案。
我们的万年历,从 2023年 开始,由于 2023年 第一天 是 星期日,将 2023年 与 月份表 一月列 对齐, 将 星期日 与 日期表 1日列 对齐,然后按照 逆时针 依次填满 星期环列,就得到上图。这样,当对齐 月份和 年份,日期表 刚好 对于 正确的 星期。
然后,需要将 2023 年之后的年份 填上,由于 星期时逆时针,故 年份 也需要按照 逆时针 排列,于是 就得到上图。
当定位到闰年时,例如:2024年,

这时月份表使用的是润一月,于是修改年份位置,让年份 对位「一月」列,这 需要 让年份位置 从 月表 的 a 列 变为 a+6列,即,顺时针挪6位,这距离不仅有点大,而且 会和该位置 的年份冲突,例如:对应 2024年 调整 后与 2041年 冲突。考虑到 a+6 ≡ a-1 (mod 7) ,于是 顺时针挪6位 就等于 逆时针挪1位,而刚好 润年导致 之后有一个空位!
按照这个分析,将所有闰年 都 逆时针挪1位,并标上 闰年标志 以和 闰月匹配,这样就得到如下结果:

最后,我们发现,年份环列上,还有6个空格,我们需要将 后续 2045 - 2050 年补上。为了方便,我们将 2023年的位置 设为 0,这样第一个空格位置就是 1,所有空位 为 5y+1。
2045年本来的位置在 2023年位置 0 处,这有冲突需要调整,由于要保持星期对应,故调整位置必须是 7x+0,于是可列出 不定方程:
7x + 0 = 5y + 1
解方程 得到 y=1(x=1),这说明可将 2045年调整到 第2个空位处。其它年份也可以用类似的方法计算,不过比较麻烦,于是可以反过来,由 空格求出 适合年份,有,

(5×1+1) mod 7 = 6 ⇒ 50
(5×2+1) mod 7 = 4 ⇒「48」
(5×3+1) mod 7 = 2 ⇒ 47
(5×4+1) mod 7 = 0 ⇒ 45
(5×5+1) mod 7 = 5 ⇒ 49
我们发现,这 其中 刚好 缺少 偏移 3 这个空位,于是 6 个空格 完美的对应 剩下的 6 个年份。按照这个结果,修改后得到:

这样,最终我们得到 2023—2050年,机械万年历的完整版本。
当然,也可以将年份设计为两层,这样可以再 容纳 2051—2078年的日历,大家有兴趣不妨试一试!
