手里只有6个向量,怎么填满64列?——对称变换的编程实践

魔表04:对称模型------从 6 个原型到 64 列矩阵

回顾

上一篇文章中,我们探究了魔表的三种对称性:旋转 \(R\)、正反互补 \(C\) 和对角镜像 \(D\)。结论是:只需要手动标定 6 个原型操作,其余 58 个效果向量全部可以用对称性推导出来。

本文就来完成这件事------从 6 个 18 维向量出发,利用 \(R\)、\(C\)、\(D\),生成完整的 \(18 \times 64\) 矩阵 \(\mathbf{A}\)。


一、列的排列约定

在开始之前,需要先约定好矩阵 \(\mathbf{A}\) 的 64 列分别对应哪 64 种操作,这样后续的求解和分析才有统一的参照。

我们采用先按按钮集分组、组内按拨轮顺序的排列方式:

列号 对应操作 说明
\(1 \sim 4\) 按钮集 \(S_1\),拨轮 UL / UR / DR / DL 第 1 组
\(5 \sim 8\) 按钮集 \(S_2\),拨轮 UL / UR / DR / DL 第 2 组
\(\cdots\) \(\cdots\) \(\cdots\)
\(61 \sim 64\) 按钮集 \(S_{16}\),拨轮 UL / UR / DR / DL 第 16 组

按钮集的 16 种排列顺序来自第三篇 4.2 节的表,按原型与其补集交替排列:

组 \(i\) 按钮集 \(S_i\) 生成方式
1 \(\varnothing\) \(v_0\)
2 \(\{\mathrm{UL},\mathrm{UR},\mathrm{DL},\mathrm{DR}\}\) \(C(v_0)\)
3 \(\{\mathrm{UL}\}\) \(v_1\)
4 \(\{\mathrm{UR},\mathrm{DL},\mathrm{DR}\}\) \(C(v_1)\)
5 \(\{\mathrm{UR}\}\) \(v_2\)
6 \(\{\mathrm{UL},\mathrm{DL},\mathrm{DR}\}\) \(C(v_2)\)
7 \(\{\mathrm{DL}\}\) \(D(v_2)\)
8 \(\{\mathrm{UL},\mathrm{UR},\mathrm{DR}\}\) \(C(D(v_2))\)
9 \(\{\mathrm{DR}\}\) \(v_3\)
10 \(\{\mathrm{UL},\mathrm{UR},\mathrm{DL}\}\) \(C(v_3)\)
11 \(\{\mathrm{UL},\mathrm{UR}\}\) \(v_4\)
12 \(\{\mathrm{DL},\mathrm{DR}\}\) \(C(v_4)\)
13 \(\{\mathrm{UL},\mathrm{DL}\}\) \(D(v_4)\)
14 \(\{\mathrm{UR},\mathrm{DR}\}\) \(C(D(v_4))\)
15 \(\{\mathrm{UL},\mathrm{DR}\}\) \(v_5\)
16 \(\{\mathrm{UR},\mathrm{DL}\}\) \(C(v_5)\)

每组内拨轮顺序固定为 \(\mathrm{UL} \to \mathrm{UR} \to \mathrm{DR} \to \mathrm{DL}\)。因此在第 \(i\) 组中:

组内位置 拨轮
第 1 列 \(\mathrm{UL}\)
第 2 列 \(\mathrm{UR}\)
第 3 列 \(\mathrm{DR}\)
第 4 列 \(\mathrm{DL}\)

二、生成算法

有了排列约定,生成 \(\mathbf{A}\) 的算法可以概括为三条流水线。

2.1 生成 UL 拨轮下的 16 个列向量

对第三篇中的 16 组按钮集,依次构造它们在 UL 拨轮下的效果向量:

复制代码
输入: v0, v1, v2, v3, v4, v5  (6个原型向量)
      C_op, D_op               (对称变换函数)

步骤:
  basis[0] = v0      ;  basis[1] = C_op(v0)
  basis[2] = v1      ;  basis[3] = C_op(v1)
  basis[4] = v2      ;  basis[5] = C_op(v2)
  basis[6] = D_op(v2) ;  basis[7] = C_op(D_op(v2))
  basis[8] = v3      ;  basis[9] = C_op(v3)
  basis[10] = v4     ;  basis[11] = C_op(v4)
  basis[12] = D_op(v4); basis[13] = C_op(D_op(v4))
  basis[14] = v5     ;  basis[15] = C_op(v5)

输出: basis[0..15]  (16个18维向量, 对应 UL 拨轮)

2.2 扩展到 4 个拨轮

对于按钮集 \(S\),它在拨轮 \(R^k(\mathrm{UL})\) 下的效果向量可由对称性公式得到:

\\\mathbf{e}_{S,\\;R\^k(\\mathrm{UL})} = R\^{\\,k}\\!\\big(\\mathbf{e}_{R\^{-k}(S),\\;\\mathrm{UL}}\\big) \\pmod{12} \\

其中 \(R^{-k}(S)\) 表示将按钮集按逆方向旋转 \(k\) 步。

复制代码
对于每个按钮集 S (k=0):
  对于每个拨轮偏移 k ← 0, 1, 2, 3:
    w   ← R^k(UL)                                 # 目标拨轮
    S0  ← R^{-k}(S)                               # 按钮集逆旋转
    vec ← basis[S0 对应的索引]                     # 查表 E(S0, UL)
    col ← R^k_op(vec)  mod 12                     # 正向旋转
    将 col 填入 A 的对应位置

2.3 三种变换函数的定义

为完成上述算法,需要定义三个变换函数。设 \(\mathbf{v} = (F_0,\dots,F_8,\; B_0,\dots,B_8)\) 是一个 18 维效果向量。

\(R^k\)(旋转 \(k \times 90^\circ\)) :正面顺时针 \(k \times 90^\circ\),反面逆时针 \(k \times 90^\circ\)。

\(C\)(正反互补) :按公式 \(C(F,\,B) = H(-B,\,-F)\) 计算,其中 \(H\) 为水平镜像(左右翻转),\(H(M){r,c}=M{r,\,2-c}\)。

\(D\)(对角镜像) :正面做转置 \(D(F){r,c} = F{c,r}\),反面做反对角转置 \(D(B){r,c} = B{2-c,\,2-r}\)。


三、程序实现

以下代码完整实现了上述算法。读者可以展开查看细节。
Python C MATLAB

python 复制代码
import numpy as np

# ============================================================
# 辅助函数: 18维向量 ⇔ (正面3×3, 反面3×3)
# ============================================================

def to_matrices(v):
    """18维向量 → (正面3×3, 反面3×3)"""
    return v[:9].reshape(3, 3), v[9:].reshape(3, 3)

def to_vector(F, B):
    """(正面3×3, 反面3×3) → 18维向量"""
    return np.concatenate([F.flatten(), B.flatten()])

# ============================================================
# 变换算子
# ============================================================

def R_op(v, k=1):
    """旋转操作 R^k: 正面顺时针k×90°, 反面逆时针k×90°"""
    F, B = to_matrices(v)
    F_new = np.rot90(F, k=-int(k))     # CW  = rot90(k=-1)
    B_new = np.rot90(B, k=int(k))      # CCW = rot90(k=+1)
    return to_vector(F_new, B_new)

def C_op(v):
    """正反互补: C(F,B) = H(-B, -F), 其中 H 为水平镜像(左右翻转)"""
    F, B = to_matrices(v)
    F_new = np.fliplr(-B)   # H(-B): 反面取负后水平镜像 → 新正面
    B_new = np.fliplr(-F)   # H(-F): 正面取负后水平镜像 → 新反面
    return to_vector(F_new, B_new)

def D_op(v):
    """对角镜像: 正面转置, 反面反对角转置"""
    F, B = to_matrices(v)
    F_new = F.T
    B_new = np.rot90(B, k=2).T       # 反对角转置 = 180°旋转后转置
    return to_vector(F_new, B_new)

# ============================================================
# 6个原型效果向量 (mod 12, 负数已转为11)
# ============================================================

v0 = np.array([1,1,1,1,1,1,1,1,1, 11,0,11,0,0,0,11,0,11], dtype=int)
v1 = np.array([1,0,0,0,0,0,0,0,0, 0,11,11,0,11,11,0,0,0], dtype=int)
v2 = np.array([1,1,0,1,1,1,1,1,1, 0,0,11,0,0,0,11,0,11], dtype=int)
v3 = np.array([1,1,1,1,1,1,1,1,0, 11,0,11,0,0,0,0,0,11], dtype=int)
v4 = np.array([1,0,1,0,0,0,0,0,0, 11,11,11,11,11,11,0,0,0], dtype=int)
v5 = np.array([1,0,0,0,0,0,0,0,1, 0,11,11,11,11,11,11,11,0], dtype=int)

# ============================================================
# 步骤1: 生成UL拨轮下全部16种按钮状态的向量
# ============================================================

ul_vectors = []
ul_labels = []

def add_entry(label, vec):
    ul_vectors.append(vec % 12)
    ul_labels.append(label)

add_entry("∅",                v0)
add_entry("{UL,UR,DL,DR}",   C_op(v0))
add_entry("{UL}",             v1)
add_entry("{UR,DL,DR}",      C_op(v1))
add_entry("{UR}",             v2)
add_entry("{UL,DL,DR}",      C_op(v2))
add_entry("{DL}",             D_op(v2))
add_entry("{UL,UR,DR}",      C_op(D_op(v2)))
add_entry("{DR}",             v3)
add_entry("{UL,UR,DL}",      C_op(v3))
add_entry("{UL,UR}",          v4)
add_entry("{DL,DR}",         C_op(v4))
add_entry("{UL,DL}",          D_op(v4))
add_entry("{UR,DR}",         C_op(D_op(v4)))
add_entry("{UL,DR}",          v5)
add_entry("{UR,DL}",         C_op(v5))

# ============================================================
# 步骤2: 扩展到4个拨轮
# ============================================================

def rotate_bits(bits, k):
    """对4-bit整数左旋转k位 (k>0=R方向: UL→UR→DR→DL)"""
    k = k % 4
    return ((bits << k) | (bits >> (4 - k))) & 0xF

# 按钮集名称 → 4-bit编码 (bit0=UL, bit1=UR, bit2=DR, bit3=DL)
def set_to_bits(name):
    mapping = {"UL": 0, "UR": 1, "DR": 2, "DL": 3}
    bits = 0
    for label, idx in mapping.items():
        if label in name:
            bits |= (1 << idx)
    return bits

# 建立 UL 向量的查找表: bits → 向量
lookup = {}
for label, vec in zip(ul_labels, ul_vectors):
    bits = set_to_bits(label)
    lookup[bits] = vec

wheel_names = ["UL", "UR", "DR", "DL"]

A = np.zeros((18, 64), dtype=int)
col = 0

for label, vec_ul in zip(ul_labels, ul_vectors):
    S_bits = set_to_bits(label)
    for k, w_name in enumerate(wheel_names):
        # 逆旋转按钮集: R^{-k}(S)
        S0_bits = rotate_bits(S_bits, -k)  # 等价于右旋转k位
        vec_S0 = lookup[S0_bits]           # E(R^{-k}(S), UL)
        e_S_w  = R_op(vec_S0, k) % 12      # R^k( E(R^{-k}(S), UL) )
        A[:, col] = e_S_w
        col += 1

print("矩阵 A 的规模:", A.shape)
print("✅ 生成完成")
c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 18维向量类型: 正面3×3 (行优先) + 反面3×3 (行优先)
typedef int Vec18[18];

// ============================================================
// 3×3 矩阵操作
// ============================================================

// 旋转 k 次 (k>0=CCW, k<0=CW)
void rot90_3x3(const int src[9], int dst[9], int k) {
    k = ((k % 4) + 4) % 4;
    if (k == 0) { memcpy(dst, src, 9 * sizeof(int)); return; }
    for (int r = 0; r < 3; r++)
        for (int c = 0; c < 3; c++) {
            switch (k) {
                case 1: dst[(2-c)*3 + r]     = src[r*3 + c]; break;  // CCW 90°
                case 2: dst[(2-r)*3 + (2-c)] = src[r*3 + c]; break;  // 180°
                case 3: dst[c*3 + (2-r)]     = src[r*3 + c]; break;  // CW 90°
            }
        }
}

void transpose_3x3(const int src[9], int dst[9]) {
    for (int r = 0; r < 3; r++)
        for (int c = 0; c < 3; c++)
            dst[c*3 + r] = src[r*3 + c];
}

void fliplr_3x3(const int src[9], int dst[9]) {
    for (int r = 0; r < 3; r++)
        for (int c = 0; c < 3; c++)
            dst[r*3 + (2-c)] = src[r*3 + c];
}

void neg_3x3(const int src[9], int dst[9]) {
    for (int i = 0; i < 9; i++) dst[i] = (-src[i]) % 12;
}

void mod12_vec(Vec18 v) {
    for (int i = 0; i < 18; i++)
        v[i] = ((v[i] % 12) + 12) % 12;
}

// ============================================================
// 对称变换算子
// ============================================================

void R_op(const Vec18 v, Vec18 out, int k) {
    int F[9], B[9], Fn[9], Bn[9];
    memcpy(F, v, 9*sizeof(int));
    memcpy(B, v+9, 9*sizeof(int));
    rot90_3x3(F, Fn, -k);  // 正面 CW
    rot90_3x3(B, Bn,  k);  // 反面 CCW
    memcpy(out, Fn, 9*sizeof(int));
    memcpy(out+9, Bn, 9*sizeof(int));
}

void C_op(const Vec18 v, Vec18 out) {
    int F[9], B[9], nF[9], nB[9];
    memcpy(F, v, 9*sizeof(int));
    memcpy(B, v+9, 9*sizeof(int));
    neg_3x3(B, nB); fliplr_3x3(nB, out);      // H(-B) → 新正面
    neg_3x3(F, nF); fliplr_3x3(nF, out+9);    // H(-F) → 新反面
}

void D_op(const Vec18 v, Vec18 out) {
    int F[9], B[9], Fn[9], Bt[9], Bn[9];
    memcpy(F, v, 9*sizeof(int));
    memcpy(B, v+9, 9*sizeof(int));
    transpose_3x3(F, Fn);
    rot90_3x3(B, Bt, 2); transpose_3x3(Bt, Bn);
    memcpy(out, Fn, 9*sizeof(int));
    memcpy(out+9, Bn, 9*sizeof(int));
}

// ============================================================
// 位操作: 按钮集 ⇔ 4-bit 编码
// ============================================================

int label_to_bits(const char *label) {
    int bits = 0;
    if (strstr(label, "UL")) bits |= (1 << 0);
    if (strstr(label, "UR")) bits |= (1 << 1);
    if (strstr(label, "DR")) bits |= (1 << 2);
    if (strstr(label, "DL")) bits |= (1 << 3);
    return bits;
}

int rotate_bits(int bits, int k) {
    k = ((k % 4) + 4) % 4;
    return ((bits << k) | (bits >> (4 - k))) & 0xF;
}

// ============================================================
// 主程序
// ============================================================

int main() {
    // 6 个原型效果向量 (mod 12, 负数已转为 11)
    Vec18 v0 = {1,1,1,1,1,1,1,1,1, 11,0,11,0,0,0,11,0,11};
    Vec18 v1 = {1,0,0,0,0,0,0,0,0, 0,11,11,0,11,11,0,0,0};
    Vec18 v2 = {1,1,0,1,1,1,1,1,1, 0,0,11,0,0,0,11,0,11};
    Vec18 v3 = {1,1,1,1,1,1,1,1,0, 11,0,11,0,0,0,0,0,11};
    Vec18 v4 = {1,0,1,0,0,0,0,0,0, 11,11,11,11,11,11,0,0,0};
    Vec18 v5 = {1,0,0,0,0,0,0,0,1, 0,11,11,11,11,11,11,11,0};

    // ---- 步骤1: 生成 UL 拨轮下全部 16 种按钮状态的向量 ----
    char  *labels[16];
    Vec18 ul_vecs[16];
    Vec18 tmp;

    #define ADD(idx, lbl, src) \
        labels[idx] = lbl; \
        memcpy(ul_vecs[idx], src, sizeof(Vec18)); \
        mod12_vec(ul_vecs[idx]);

    ADD(0,  "∅",                v0);
    C_op(v0, tmp);     ADD(1,  "{UL,UR,DL,DR}",   tmp);
    ADD(2,  "{UL}",             v1);
    C_op(v1, tmp);     ADD(3,  "{UR,DL,DR}",      tmp);
    ADD(4,  "{UR}",             v2);
    C_op(v2, tmp);     ADD(5,  "{UL,DL,DR}",      tmp);
    D_op(v2, tmp);     ADD(6,  "{DL}",             tmp);
    C_op(tmp, tmp);    ADD(7,  "{UL,UR,DR}",      tmp);
    ADD(8,  "{DR}",             v3);
    C_op(v3, tmp);     ADD(9,  "{UL,UR,DL}",      tmp);
    ADD(10, "{UL,UR}",          v4);
    C_op(v4, tmp);     ADD(11, "{DL,DR}",         tmp);
    D_op(v4, tmp);     ADD(12, "{UL,DL}",          tmp);
    C_op(tmp, tmp);    ADD(13, "{UR,DR}",         tmp);
    ADD(14, "{UL,DR}",          v5);
    C_op(v5, tmp);     ADD(15, "{UR,DL}",         tmp);

    #undef ADD

    // 查找表: bits(0~15) → 向量
    Vec18 lookup[16];
    for (int i = 0; i < 16; i++) {
        int bits = label_to_bits(labels[i]);
        memcpy(lookup[bits], ul_vecs[i], sizeof(Vec18));
    }

    // ---- 步骤2: 扩展到 4 个拨轮 ----
    const char *wheels[] = {"UL", "UR", "DR", "DL"};
    int A[18][64];
    int col = 0;

    for (int i = 0; i < 16; i++) {
        int S_bits = label_to_bits(labels[i]);
        for (int k = 0; k < 4; k++) {
            int S0_bits = rotate_bits(S_bits, -k);   // R^{-k}(S)
            Vec18 tmp2;
            R_op(lookup[S0_bits], tmp2, k);          // R^k( E(R^{-k}(S), UL) )
            mod12_vec(tmp2);
            for (int r = 0; r < 18; r++)
                A[r][col] = tmp2[r];
            col++;
        }
    }

    printf("矩阵 A 的规模: 18 × %d\n", col);
    printf("✅ 生成完成\n");
    return 0;
}
matlab 复制代码
% 生成魔表 18×64 操作矩阵 A (mod 12)
% 从 6 个原型向量出发,利用 R/D/C 对称算子扩展到全部 64 列

clear;

% ---- 6 个原型效果向量 (mod 12, 负数已转为 11) ----
v0 = [ 1, 1, 1, 1, 1, 1, 1, 1, 1,  11, 0, 11, 0, 0, 0, 11, 0, 11];
v1 = [ 1, 0, 0, 0, 0, 0, 0, 0, 0,  0, 11, 11, 0, 11, 11, 0, 0, 0];
v2 = [ 1, 1, 0, 1, 1, 1, 1, 1, 1,  0, 0, 11, 0, 0, 0, 11, 0, 11];
v3 = [ 1, 1, 1, 1, 1, 1, 1, 1, 0,  11, 0, 11, 0, 0, 0, 0, 0, 11];
v4 = [ 1, 0, 1, 0, 0, 0, 0, 0, 0,  11, 11, 11, 11, 11, 11, 0, 0, 0];
v5 = [ 1, 0, 0, 0, 0, 0, 0, 0, 1,  0, 11, 11, 11, 11, 11, 11, 11, 0];

% ---- 步骤 1: 生成 UL 拨轮下全部 16 种按钮状态的向量 ----
ul_labels = cell(1, 16);
ul_vecs   = zeros(16, 18);

ul_labels{1}  = '∅';                ul_vecs(1,:)  = mod(v0, 12);
ul_labels{2}  = '{UL,UR,DL,DR}';   ul_vecs(2,:)  = mod(C_op(v0), 12);
ul_labels{3}  = '{UL}';             ul_vecs(3,:)  = mod(v1, 12);
ul_labels{4}  = '{UR,DL,DR}';      ul_vecs(4,:)  = mod(C_op(v1), 12);
ul_labels{5}  = '{UR}';             ul_vecs(5,:)  = mod(v2, 12);
ul_labels{6}  = '{UL,DL,DR}';      ul_vecs(6,:)  = mod(C_op(v2), 12);
ul_labels{7}  = '{DL}';             ul_vecs(7,:)  = mod(D_op(v2), 12);
ul_labels{8}  = '{UL,UR,DR}';      ul_vecs(8,:)  = mod(C_op(D_op(v2)), 12);
ul_labels{9}  = '{DR}';             ul_vecs(9,:)  = mod(v3, 12);
ul_labels{10} = '{UL,UR,DL}';      ul_vecs(10,:) = mod(C_op(v3), 12);
ul_labels{11} = '{UL,UR}';          ul_vecs(11,:) = mod(v4, 12);
ul_labels{12} = '{DL,DR}';         ul_vecs(12,:) = mod(C_op(v4), 12);
ul_labels{13} = '{UL,DL}';          ul_vecs(13,:) = mod(D_op(v4), 12);
ul_labels{14} = '{UR,DR}';         ul_vecs(14,:) = mod(C_op(D_op(v4)), 12);
ul_labels{15} = '{UL,DR}';          ul_vecs(15,:) = mod(v5, 12);
ul_labels{16} = '{UR,DL}';         ul_vecs(16,:) = mod(C_op(v5), 12);

% 查找表: bits(0~15) + 1 → 向量
lookup = cell(1, 16);
for i = 1:16
    bits = label_to_bits(ul_labels{i});
    lookup{bits + 1} = ul_vecs(i, :);
end

% ---- 步骤 2: 扩展到 4 个拨轮 ----
wheel_names = ["UL", "UR", "DR", "DL"];
A = zeros(18, 64);

col = 1;
for i = 1:16
    S_bits = label_to_bits(ul_labels{i});
    for k = 1:4
        S0_bits = rotate_bits(S_bits, -(k - 1));        % R^{-k}(S)
        A(:, col) = mod(R_op(lookup{S0_bits + 1}, k - 1), 12);
        col = col + 1;
    end
end

fprintf('矩阵 A 的规模: %d × %d\n', size(A, 1), size(A, 2));
fprintf('✅ 生成完成\n');


% ============================================================
% 局部函数
% ============================================================

function [F, B] = to_matrices(v)
    % 18 向量 → 正面 3×3, 反面 3×3 (行优先等价于 Python reshape)
    F = reshape(v(1:9), [3, 3]).';
    B = reshape(v(10:18), [3, 3]).';
end

function v = to_vector(F, B)
    v = [reshape(F.', [1, 9]), reshape(B.', [1, 9])];
end

function out = R_op(v, k)
    [F, B] = to_matrices(v);
    out = to_vector(rot90(F, -k), rot90(B, k));
end

function out = C_op(v)
    [F, B] = to_matrices(v);
    out = to_vector(fliplr(-B), fliplr(-F));
end

function out = D_op(v)
    [F, B] = to_matrices(v);
    out = to_vector(F.', rot90(B, 2).');
end

function bits = label_to_bits(s)
    bits = uint32(0);
    if contains(s, 'UL'), bits = bitor(bits, 1); end
    if contains(s, 'UR'), bits = bitor(bits, 2); end
    if contains(s, 'DR'), bits = bitor(bits, 4); end
    if contains(s, 'DL'), bits = bitor(bits, 8); end
end

function out = rotate_bits(bits, k)
    k = mod(k, 4);
    if k == 0
        out = bits;
    else
        b = uint32(bits);
        left  = bitand(bitshift(b, k), 15);
        right = bitand(bitshift(b, k - 4), 15);
        out   = double(bitor(left, right));
    end
end

注:此代码由 AI 辅助完成,已人工审查正确性。


四、矩阵 \(\mathbf{A}\) 的完整输出

展开下方可查看完整的 \(18 \times 64\) 矩阵。每行对应一个表盘指针,每列对应一种基本操作。
点击展开矩阵 A(18 行 × 64 列,mod 12)

复制代码
列 1: ( 1,  1,  1,  1,  1,  1,  1,  1,  1, 11,  0, 11,  0,  0,  0, 11,  0, 11)   empty, UL
列 2: ( 1,  1,  1,  1,  1,  1,  1,  1,  1, 11,  0, 11,  0,  0,  0, 11,  0, 11)   empty, UR
列 3: ( 1,  1,  1,  1,  1,  1,  1,  1,  1, 11,  0, 11,  0,  0,  0, 11,  0, 11)   empty, DR
列 4: ( 1,  1,  1,  1,  1,  1,  1,  1,  1, 11,  0, 11,  0,  0,  0, 11,  0, 11)   empty, DL
列 5: ( 1,  0,  1,  0,  0,  0,  1,  0,  1, 11, 11, 11, 11, 11, 11, 11, 11, 11)   {UL,UR,DL,DR}, UL
列 6: ( 1,  0,  1,  0,  0,  0,  1,  0,  1, 11, 11, 11, 11, 11, 11, 11, 11, 11)   {UL,UR,DL,DR}, UR
列 7: ( 1,  0,  1,  0,  0,  0,  1,  0,  1, 11, 11, 11, 11, 11, 11, 11, 11, 11)   {UL,UR,DL,DR}, DR
列 8: ( 1,  0,  1,  0,  0,  0,  1,  0,  1, 11, 11, 11, 11, 11, 11, 11, 11, 11)   {UL,UR,DL,DR}, DL
列 9: ( 1,  0,  0,  0,  0,  0,  0,  0,  0,  0, 11, 11,  0, 11, 11,  0,  0,  0)   {UL}, UL
列10: ( 0,  1,  1,  1,  1,  1,  1,  1,  1, 11,  0,  0,  0,  0,  0, 11,  0, 11)   {UL}, UR
列11: ( 0,  1,  1,  1,  1,  1,  1,  1,  1, 11,  0,  0,  0,  0,  0, 11,  0, 11)   {UL}, DR
列12: ( 0,  1,  1,  1,  1,  1,  1,  1,  1, 11,  0,  0,  0,  0,  0, 11,  0, 11)   {UL}, DL
列13: ( 1,  1,  0,  1,  1,  0,  0,  0,  0,  0,  0, 11,  0,  0,  0,  0,  0,  0)   {UR,DL,DR}, UL
列14: ( 0,  0,  1,  0,  0,  0,  1,  0,  1, 11, 11,  0, 11, 11, 11, 11, 11, 11)   {UR,DL,DR}, UR
列15: ( 0,  0,  1,  0,  0,  0,  1,  0,  1, 11, 11,  0, 11, 11, 11, 11, 11, 11)   {UR,DL,DR}, DR
列16: ( 0,  0,  1,  0,  0,  0,  1,  0,  1, 11, 11,  0, 11, 11, 11, 11, 11, 11)   {UR,DL,DR}, DL
列17: ( 1,  1,  0,  1,  1,  1,  1,  1,  1,  0,  0, 11,  0,  0,  0, 11,  0, 11)   {UR}, UL
列18: ( 0,  0,  1,  0,  0,  0,  0,  0,  0, 11, 11,  0, 11, 11,  0,  0,  0,  0)   {UR}, UR
列19: ( 1,  1,  0,  1,  1,  1,  1,  1,  1,  0,  0, 11,  0,  0,  0, 11,  0, 11)   {UR}, DR
列20: ( 1,  1,  0,  1,  1,  1,  1,  1,  1,  0,  0, 11,  0,  0,  0, 11,  0, 11)   {UR}, DL
列21: ( 1,  0,  0,  0,  0,  0,  1,  0,  1,  0, 11, 11, 11, 11, 11, 11, 11, 11)   {UL,DL,DR}, UL
列22: ( 0,  1,  1,  0,  1,  1,  0,  0,  0, 11,  0,  0,  0,  0,  0,  0,  0,  0)   {UL,DL,DR}, UR
列23: ( 1,  0,  0,  0,  0,  0,  1,  0,  1,  0, 11, 11, 11, 11, 11, 11, 11, 11)   {UL,DL,DR}, DR
列24: ( 1,  0,  0,  0,  0,  0,  1,  0,  1,  0, 11, 11, 11, 11, 11, 11, 11, 11)   {UL,DL,DR}, DL
列25: ( 1,  1,  1,  1,  1,  1,  0,  1,  1, 11,  0, 11,  0,  0,  0, 11,  0,  0)   {DL}, UL
列26: ( 1,  1,  1,  1,  1,  1,  0,  1,  1, 11,  0, 11,  0,  0,  0, 11,  0,  0)   {DL}, UR
列27: ( 1,  1,  1,  1,  1,  1,  0,  1,  1, 11,  0, 11,  0,  0,  0, 11,  0,  0)   {DL}, DR
列28: ( 0,  0,  0,  0,  0,  0,  1,  0,  0,  0,  0,  0,  0, 11, 11,  0, 11, 11)   {DL}, DL
列29: ( 1,  0,  1,  0,  0,  0,  0,  0,  1, 11, 11, 11, 11, 11, 11, 11, 11,  0)   {UL,UR,DR}, UL
列30: ( 1,  0,  1,  0,  0,  0,  0,  0,  1, 11, 11, 11, 11, 11, 11, 11, 11,  0)   {UL,UR,DR}, UR
列31: ( 1,  0,  1,  0,  0,  0,  0,  0,  1, 11, 11, 11, 11, 11, 11, 11, 11,  0)   {UL,UR,DR}, DR
列32: ( 0,  0,  0,  1,  1,  0,  1,  1,  0,  0,  0,  0,  0,  0,  0,  0,  0, 11)   {UL,UR,DR}, DL
列33: ( 1,  1,  1,  1,  1,  1,  1,  1,  0, 11,  0, 11,  0,  0,  0,  0,  0, 11)   {DR}, UL
列34: ( 1,  1,  1,  1,  1,  1,  1,  1,  0, 11,  0, 11,  0,  0,  0,  0,  0, 11)   {DR}, UR
列35: ( 0,  0,  0,  0,  0,  0,  0,  0,  1,  0,  0,  0, 11, 11,  0, 11, 11,  0)   {DR}, DR
列36: ( 1,  1,  1,  1,  1,  1,  1,  1,  0, 11,  0, 11,  0,  0,  0,  0,  0, 11)   {DR}, DL
列37: ( 1,  0,  1,  0,  0,  0,  1,  0,  0, 11, 11, 11, 11, 11, 11,  0, 11, 11)   {UL,UR,DL}, UL
列38: ( 1,  0,  1,  0,  0,  0,  1,  0,  0, 11, 11, 11, 11, 11, 11,  0, 11, 11)   {UL,UR,DL}, UR
列39: ( 0,  0,  0,  0,  1,  1,  0,  1,  1,  0,  0,  0,  0,  0,  0, 11,  0,  0)   {UL,UR,DL}, DR
列40: ( 1,  0,  1,  0,  0,  0,  1,  0,  0, 11, 11, 11, 11, 11, 11,  0, 11, 11)   {UL,UR,DL}, DL
列41: ( 1,  0,  1,  0,  0,  0,  0,  0,  0, 11, 11, 11, 11, 11, 11,  0,  0,  0)   {UL,UR}, UL
列42: ( 1,  0,  1,  0,  0,  0,  0,  0,  0, 11, 11, 11, 11, 11, 11,  0,  0,  0)   {UL,UR}, UR
列43: ( 0,  0,  0,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0, 11,  0, 11)   {UL,UR}, DR
列44: ( 0,  0,  0,  1,  1,  1,  1,  1,  1,  0,  0,  0,  0,  0,  0, 11,  0, 11)   {UL,UR}, DL
列45: ( 1,  1,  1,  1,  1,  1,  0,  0,  0, 11,  0, 11,  0,  0,  0,  0,  0,  0)   {DL,DR}, UL
列46: ( 1,  1,  1,  1,  1,  1,  0,  0,  0, 11,  0, 11,  0,  0,  0,  0,  0,  0)   {DL,DR}, UR
列47: ( 0,  0,  0,  0,  0,  0,  1,  0,  1,  0,  0,  0, 11, 11, 11, 11, 11, 11)   {DL,DR}, DR
列48: ( 0,  0,  0,  0,  0,  0,  1,  0,  1,  0,  0,  0, 11, 11, 11, 11, 11, 11)   {DL,DR}, DL
列49: ( 1,  0,  0,  0,  0,  0,  1,  0,  0,  0, 11, 11,  0, 11, 11,  0, 11, 11)   {UL,DL}, UL
列50: ( 0,  1,  1,  0,  1,  1,  0,  1,  1, 11,  0,  0,  0,  0,  0, 11,  0,  0)   {UL,DL}, UR
列51: ( 0,  1,  1,  0,  1,  1,  0,  1,  1, 11,  0,  0,  0,  0,  0, 11,  0,  0)   {UL,DL}, DR
列52: ( 1,  0,  0,  0,  0,  0,  1,  0,  0,  0, 11, 11,  0, 11, 11,  0, 11, 11)   {UL,DL}, DL
列53: ( 1,  1,  0,  1,  1,  0,  1,  1,  0,  0,  0, 11,  0,  0,  0,  0,  0, 11)   {UR,DR}, UL
列54: ( 0,  0,  1,  0,  0,  0,  0,  0,  1, 11, 11,  0, 11, 11,  0, 11, 11,  0)   {UR,DR}, UR
列55: ( 0,  0,  1,  0,  0,  0,  0,  0,  1, 11, 11,  0, 11, 11,  0, 11, 11,  0)   {UR,DR}, DR
列56: ( 1,  1,  0,  1,  1,  0,  1,  1,  0,  0,  0, 11,  0,  0,  0,  0,  0, 11)   {UR,DR}, DL
列57: ( 1,  0,  0,  0,  0,  0,  0,  0,  1,  0, 11, 11, 11, 11, 11, 11, 11,  0)   {UL,DR}, UL
列58: ( 0,  1,  1,  1,  1,  1,  1,  1,  0, 11,  0,  0,  0,  0,  0,  0,  0, 11)   {UL,DR}, UR
列59: ( 1,  0,  0,  0,  0,  0,  0,  0,  1,  0, 11, 11, 11, 11, 11, 11, 11,  0)   {UL,DR}, DR
列60: ( 0,  1,  1,  1,  1,  1,  1,  1,  0, 11,  0,  0,  0,  0,  0,  0,  0, 11)   {UL,DR}, DL
列61: ( 1,  1,  0,  1,  1,  1,  0,  1,  1,  0,  0, 11,  0,  0,  0, 11,  0,  0)   {UR,DL}, UL
列62: ( 0,  0,  1,  0,  0,  0,  1,  0,  0, 11, 11,  0, 11, 11, 11,  0, 11, 11)   {UR,DL}, UR
列63: ( 1,  1,  0,  1,  1,  1,  0,  1,  1,  0,  0, 11,  0,  0,  0, 11,  0,  0)   {UR,DL}, DR
列64: ( 0,  0,  1,  0,  0,  0,  1,  0,  0, 11, 11,  0, 11, 11, 11,  0, 11, 11)   {UR,DL}, DL

如下表所示,可直观查看完整的 \(18 \times 64\) 矩阵。


五、结语

本文完成了从 6 个原型到 \(\mathbf{A}\) 矩阵的完整生成过程:

  1. 排列约定:64 列按 16 组按钮集 × 4 个拨轮的有序排列,为后续分析建立统一坐标。
  2. 生成算法 :三步流水线------原型展开 16 个 UL 向量 → 查表加旋转扩展到 4 个拨轮 → 填满 \(\mathbf{A}\)。
  3. 程序实现 :给出了完整的 Python、C 及 MATLAB 代码,配合对称变换 \(R\)、\(C\)、\(D\) 的矩阵运算实现。

至此,魔表的数学模型从"纸面上的公式"走到了"可运行的代码"。

下一篇,我们将对 \(\mathbf{A}\) 进行深入的数学分析------秩、不变量、可达状态空间,真正理解这个 \(18 \times 64\) 矩阵背后的结构。