64 种操作,18 个方程——魔表的朴素线性模型

魔表02:64 种操作的线性方程组模型

回顾

上一篇文章中,我们认识了魔表的基本面貌,并总结了几条关键数学性质:

  • 指针状态是模 12 的离散量
  • 操作效果与当前状态无关
  • 操作效果可以线性叠加,且操作顺序可交换
  • 逆时针转动等价于顺时针取负

这些性质共同指向了一个事实:魔表的指针变化本质上是一个模 12 上的线性系统

本文通过建立一个数学模型,来把这个线性系统的完整面貌展现出来。


一、数学建模

1.1 状态向量

魔表正反面各有 9 个表盘,共 18 根指针。我们沿用上一篇的编号约定

\\\mathbf{x} = (F_1, F_2, \\dots, F_9,\\; B_1, B_2, \\dots, B_9)\^T \\in (\\mathbb{Z}_{12})\^{18} \\

其中 \(\mathbb{Z}_{12} = \mathbb{Z} / 12\mathbb{Z}\) 是模 12 整数环。目标状态是全 12 点,即零向量 \(\mathbf{0}\)。

1.2 基本操作

一个基本操作由两个因素决定:

  • 按钮状态 \(S\):4 个按钮各有按下/弹起两种状态,共 \(2^4 = 16\) 种
  • 拨轮选择 \(w \in \{\mathrm{UL}, \mathrm{UR}, \mathrm{DR}, \mathrm{DL}\}\):4 种

所以一共有 \(16 \times 4 = 64\) 种"按钮-拨轮"组合。

每种组合 \((S, w)\) 对应一个固定的效果向量 \(\mathbf{e}{S,w} \in (\mathbb{Z}{12})^{18}\)。它的含义是:

在按钮状态 \(S\) 下,顺时针转动拨轮 \(w\) 恰好 1 格对 18 根指针造成的变化量。

1.3 多格转动与逆时针

由于性质 3(线性叠加),顺时针转动 \(k\) 格的效果就是 \(k \cdot \mathbf{e}_{S,w}\)。

由于性质 4(逆时针取负),逆时针转动 \(k\) 格等价于顺时针转动 \(12 - k\) 格,效果为 \((12 - k) \cdot \mathbf{e}_{S,w}\)。

因此,我们不需要为逆时针单独建向量。把所有操作的效果都表示成 \(\mathbf{e}_{S,w}\) 的整数倍即可。

1.4 总效果方程

设变量 \(k_{S,w} \in \mathbb{Z}_{12}\) 表示在按钮状态 \(S\) 下,顺时针转动拨轮 \(w\) 的格数

从当前状态 \(\mathbf{x}\) 出发,所有操作的总效果 \(\mathbf{y}\) 为:

\\\mathbf{y} = \\sum_{S \\; \\subseteq \\; \\{\\mathrm{UL,UR,DR,DL}\\}} \\; \\sum_{w \\;\\in\\; \\{\\mathrm{UL,UR,DR,DL}\\}} k_{S,w} \\cdot \\mathbf{e}_{S,w} \\pmod{12} \\

要回到全 12 点,需要总效果恰好抵消当前状态:

\\\mathbf{x} + \\mathbf{y} \\equiv 0 \\pmod{12} \\

即:

\\\sum_{S,w} k_{S,w} \\cdot \\mathbf{e}_{S,w} \\equiv -\\mathbf{x} \\pmod{12} \\

这就是魔表求解问题的核心方程。

对于 64 种基本操作的效果向量 \(\mathbf{e}{S,w}\) 都是已知且唯一确定的,因此我们要求的就是这 64 种基本操作对应的 64 个转动格子数 \(k{S,w}\) 。换句话说,这就是一个有 18 个方程、64 个未知数( \(k_{S,w}\) )的模 12 线性方程组,我们只需要采用线性代数的手段,求解这个线性方程组即可得出这 64 种基本操作每个应该转动的格数。这样,也就能够找到还原这个魔表的方法了。


二、矩阵形式

把 64 个效果向量排成一个矩阵:

\\\mathbf{A} = \\big\[\\; \\mathbf{e}_{S_1,w_1} \\;\\; \\mathbf{e}_{S_2,w_2} \\;\\; \\cdots \\;\\; \\mathbf{e}_{S_{64},w_{64}} \\; \\big \]

\(\mathbf{A}\) 是一个 \(18 \times 64\) 的矩阵,每列的 \(\mathbf{e}_{S,w}\) 都是一个18维的效果向量。这个矩阵记录的信息是 64 种基础操作中每种操作对应的 18 个表盘指针的变动量。

把所有变量 \(k_{S,w}\) 排成一个 64 维列向量:

\\\mathbf{k} = (k_{S_1,w_1}, k_{S_2,w_2}, \\dots, k_{S_{64},w_{64}})\^T \\in (\\mathbb{Z}_{12})\^{64} \\

则核心方程写成紧凑的矩阵形式:

\\\boxed{\\;\\mathbf{A} \\mathbf{k} \\equiv -\\mathbf{x} \\pmod{12}\\;} \\

整个问题变成了:给定状态 \(\mathbf{x}\),在模 12 下求解 \(\mathbf{k}\)。


三、如何标定 \(\mathbf{A}\)

构建矩阵 \(\mathbf{A}\) 需要我们逐一测出 \(64\) 个效果向量。方法很直接:

对每一个按钮-拨轮组合 \((S, w)\):

  1. 将魔表恢复到全 \(12\) 点状态
  2. 把按钮设置成 \(S\)
  3. 顺时针转动拨轮 \(w\) 恰好 \(1\) 格
  4. 记录 \(18\) 根指针当前指向的点数(这就等价于记录了每个指针各自变化了多少小时(顺时针记 \(+1\),逆时针记 \(-1\)))
  5. 这组变化量就是 \(\mathbf{e}_{S,w}\)

重复 \(64\) 次,填满矩阵 \(\mathbf{A}\) 的 \(64\) 列。

举个例子

例如我们把 只按下左上角按钮(UL),转动左上角拨轮(UL) 规定为第一种基本操作 \((S_1,w_1)\) 。那么在恢复状态下顺时针拨动一格,实际结果如下图:

可以观察到,从正面看,只有左上角按钮按下去,效果是只有左上角的指针顺时针转动 1 小时;从反面看,相当于是只有右上角的按钮抬起,效果是右上角的四个指针都逆时针转动了 1 小时。

即当前盘面情况为(把12点写作0点):

\\\text{正面:}\\quad \\begin{matrix} 1 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \\\\ 0 \& 0 \& 0 \\end{matrix} \\qquad \\text{反面:}\\quad \\begin{matrix} 0 \& 11 \& 11 \\\\ 0 \& 11 \& 11 \\\\ 0 \& 0 \& 0 \\end{matrix} \\

用数学的语言来表达,这个效果向量就应该写为:

\\\mathbf{e}_{S_1,w_1} = (1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11, 11, 0, 11, 11, 0, 0, 0)\^T \\

这也就是矩阵 \(\mathbf{A}\) 的第一列数据了。以此类推,我们需要挨个测试总共64种基础操作,把他们对应的指针变换效果全都表示出来,形成完整的矩阵 \(\mathbf{A}\) 。


四、如何求解

得到 \(\mathbf{A}\) 后,解模 12 线性方程组 \(\mathbf{A}\mathbf{k} \equiv -\mathbf{x} \pmod{12}\),就能得到一组操作方案。

由于模 12 不是域,不能直接用高斯消元。常用的处理方式有两种:

  1. Smith 标准型 :对 \(\mathbf{A}\) 在整数环上做 Smith 标准型分解,统一处理模 12 的约束。
  2. CRT 拆分 :利用中国剩余定理 \(\mathbb{Z}_{12} \cong \mathbb{Z}_3 \times \mathbb{Z}_4\),分别在模 3 和模 4 下求解(此时模数是质数或质数幂),再合并。模 3 下可直接高斯消元,模 4 下稍复杂但也比直接解模 12 容易。

两种方法都是标准的线性代数技术,本文暂不展开算法的具体实现。

解出 \(\mathbf{k}\) 后,对于每个分量 \(k_{S,w}\) :

  • 若 \(k_{S,w} = 0\):不执行该操作
  • 若 \(1 \le k_{S,w} \le 6\):在按钮状态 \(S\) 下,顺时针转动拨轮 \(w\) 正好 \(k_{S,w}\) 格
  • 若 \(7 \le k_{S,w} \le 11\):在按钮状态 \(S\) 下,逆时针转动拨轮 \(w\) 正好( \(12 - k_{S,w}\) )格

由于操作可交换,这些步骤可以按任意顺序执行。这样按照计算出来的 \(\mathbf{k}\) 转动之后,魔表就会被完全复原了。


五、一个具体状态来走一遍

为了更直观地说明这套流程,我们不妨用一个例子来走一遍。

在这个图片当中,展示了一种打乱状态下的魔表,我们首先要做的是把每个表盘指针指向的点数读出来。

\\\text{正面:}\\quad \\begin{matrix} 8 \& 2 \& 9 \\\\ 4 \& 1 \& 12 \\\\ 2 \& 4 \& 10 \\end{matrix} \\qquad \\text{反面:}\\quad \\begin{matrix} 3 \& 9 \& 4 \\\\ 3 \& 11 \& 6 \\\\ 2 \& 3 \& 10 \\end{matrix} \\

我们接下来就把这 18 个数字按照我们之前定义的顺序,写成一个列向量(不要忘记把 12 改成 0 哦),这就是我们的初始状态 \(\mathbf{x}\) 。

\\\mathbf{x} = (8, 2, 9, 4, 1, 0, 2, 4, 10, 3, 9, 4, 3, 11, 6, 2, 3, 10)\^T \\

我们的目标是找到一组操作 \(k_{S,w}\),使 \(\mathbf{A}\mathbf{k} \equiv -\mathbf{x} \pmod{12}\),即:

\\\mathbf{A}\\mathbf{k} \\equiv (4, 10, 3, 8, 11, 0, 10, 8, 2,\\; 9, 3, 8, 9, 1, 6, 10, 9, 2)\^T \\pmod{12} \\

\(\mathbf{A}\) 是我们辛苦标定出来的一个 \(18 \times 64\) 的矩阵,带入式子中,我们利用线性代数的方法来求解 \(\mathbf{k}\) 即可。

倘若我们求解出的 \(\mathbf{k}\) 结果如下(此处随便举的例子):

\\\mathbf{k} = (2, 9, 0, \\cdots 0, 8)\^T \\

那么我们复原这个魔表的操作就是:

  1. 对于第一个基础操作,我们要顺时针转动 2 格;

  2. 对于第二个基础操作,我们要逆时针转动 3 格;

  3. 对于第三个基础操作,我们直接跳过,无需操作;

......

  1. 对于第六十四个基础操作,我们要逆时针转动 4 格。

由于第一篇提到的性质3,这 64 种操作的执行顺序无关最后的结果,都能复原整个魔表。


六、反思:这个方法的问题在哪里

\(\qquad\)上述方法在数学上看起来没有任何问题。\(18\) 个方程,\(64\) 个未知数,解空间很大,总能找到一组解。

\(\qquad\)但它的工程问题 在于:标定 64 个效果向量太费劲了

\(\qquad\)你要对着魔表,逐一设置 64 种按钮组合、逐一转动拨轮、逐一记录 18 根指针的变化量。64 次手工标定,每次要记录 18 个数字,稍有不慎就会出错。

\(\qquad\)更重要的是,你只要把玩魔表几分钟就会发现------这 64 种操作根本不是互相独立的。 很多操作的效果矩阵,看起来就是前几个操作的"镜像"或"旋转"。例如,在只按下 UL,转动 UL 拨轮 的效果,和在只按下 UR,转动 UR 拨轮的效果,似乎只是把整个盘面旋转 90° 的结果。

\(\qquad\)这暗示了一个关键事实:我们可以利用魔表结构上的对称性,从少数几个基础操作,推导出全部 64 个效果向量

\(\qquad\)下一篇文章,我们将探究魔表的对称性,利用对称性这一强有力的工具,将手动标定64个效果向量的过程大幅简化。