群论基础

群论基础

一、群的定义

若集合 \(G \neq \varnothing\)\(G\) 上的运算 \(\cdot\) 构成的代数结构 \((G, \cdot)\) 称为一个群,则需要满足以下三条性质:

(1) 有结合律 \(\forall a, b, c \in G, (a \cdot b) \cdot c = a \cdot (b \cdot c)\)

(2) 有左单位元 \(\exists e, \forall a \in G, e \cdot a = a\)

(3) 有左逆元 \(\forall a \in G, \exists b, b \cdot a = e\)

通常简记 \(a \cdot b\)\(ab\)

通过这三条性质可以推导以下结论

存在右逆元:考虑 \(ba = e\),可得 \(bab = b\),左乘 \(b\) 的左逆元,可得 \(ab = e\)

存在右单位元:考虑 \(ab = ba = e\),可得 \(ae = aba = ea = a\)

逆元唯一:\(ab_1 = e = ab_2\),左乘 \(a\) 的左逆元,可得 \(b_1 = b_2\)

单位元唯一:\(e_1 = e_1e_2 = e_2\)

逆元的逆元:\(a\)\({a ^ {-1}} ^ {-1}\) 都是 \(a ^ {-1}\) 的逆元,故 \(a = {a ^ {-1}} ^ {-1}\)

消去律:\(ab = ac \implies a ^ {-1}ab = a ^ {-1}ac \implies b = c\)

乘法的逆元:\(ab(ab) ^ {-1} = e \implies (ab) ^ {-1} = b ^ {-1} a ^ {-1}\)

故若记 \(a ^ {-1}\)\(a\) 的逆元,可说明 \((aa) ^ {-1} = a ^ {-1} a ^ {-1}\),故可记

\[a ^ m = \begin{cases} a ^ {m - 1} a & \qquad (m > 0) \\ e & \qquad (m = 0) \\ a ^ {m + 1} a ^ {-1} & \qquad (m < 0) \end{cases}\]

而没有歧义

如果一个运算仅满足结合律,有左单位元与右逆元,仍可能不是群

考虑所有形如 \(\displaystyle \begin{bmatrix} a & b \\ 0 & 0 \end{bmatrix}, a \neq 0\) 的矩阵,有左单位元 \(\displaystyle \begin{bmatrix} 1 & 0 \\ 0 & 0 \end{bmatrix}\),右逆元 \(\displaystyle \begin{bmatrix} a ^ {-1} & 0 \\ 0 & 0 \end{bmatrix}\),但如果 \(b \neq 0\),则没有左逆元

如果这个运算还满足交换律,即 \(a \cdot b = b \cdot a\),则称其为阿贝尔 (Abel) 群

二、群的基本概念

子群 (subgroup)

子群:群 \((G, \cdot), (H, \cdot)\) 满足 \(H \subseteq G\),则 \((H, \cdot)\)\((G, \cdot)\) 的子群,可以记作 \(H \leq G\),若有 \(H \neq G\),则称为真子群 (proper subgroup),记作 \(H < G\)

子群 \(H\) 具有 \(G\) 的元素和操作,这意味着 \(H\) 包含 \(G\) 的单位元,并且 \(\forall h_1, h_2 \in H, h_1h_2, h_1 ^ {-1} \in H\)

\(G\) 的子集 \(H\) 是子群有一个充要条件:\(\forall h_1, h_2 \in H, h_1 ^ {-1} h_2 \in H\)

生成子群 (generated subgroup)

\(S \subset G\) 的生成子群 \(\langle S \rangle\)\(G\) 的包含 \(S\) 的最小子群,称 \(S\) 为群的生成集 (generating set of a group)

如果 \(G = \langle S \rangle\),我们称 \(S\) 生成 \(G\)\(S\) 中的元素叫做群的生成元 (generator)

陪集 (coset) 和指数 (index)

陪集是一个群的子集,它包含通过将群的一个固定元素乘以给定子群的每个元素得到的乘积

用符号表示,包含元素 \(g\)\(H\) 的左右陪集分别是

\[\begin{aligned} gH & = \{g \cdot h \mid h \in H\} \\ Hg & = \{h \cdot g \mid h \in H\} \end{aligned}\]

可以证明 \(H\) 的左右陪集数相等,构造映射 \(\varphi: aH \mapsto Ha ^ {-1}, \forall a \in G\)

首先,\(\varphi\) 是一个映射,即 \(aH = bH \iff ah_1 = bh_2 \iff (ah_1) ^ {-1} = (bh_2) ^ {-1} \iff h_1 ^ {-1} a ^ {-1} = h_2 ^ {-1} b ^ {-1} \iff Ha ^ {-1} = Hb ^ {-1}\),并且依此可得其为单射

并且 \(\forall Ha, \varphi (a ^ {-1} H) = Ha\),故 \(\varphi\) 为双射

从而可以令 \([G : H]\) 表示 \(G\)\(H\) 的陪集数,称为 \(H\)\(G\) 中的指数

阶 (order)

\(G\) 的阶是它元素的个数,记作 \(\operatorname{ord}(G)\)\(|G|\),无限群有无限阶

\(G\) 内的一个元素 \(a\) 的阶是使 \(a ^ m = e\) 成立的最小正整数 \(m\),记作 \(\operatorname{ord}(a)\)\(|a|\),等于 \(|\langle a \rangle|\),若这个数不存在,则称 \(a\) 有无限阶

拉格朗日 (Lagrange) 定理

如果 \(H \leq G\),那么 \(|G| = [G : H] |H|\)

首先,可以证明各陪集所含元素相等,即 \(\forall H \le G, a \in G, |aH| = |H|\)

构造 \(\varphi: h \mapsto ah\),则由 \(ah_1 = ah_2 \implies h_1 = h_2\) 得其为单射,\(\varphi(a ^ {-1} h) = h\) 得其是满射,得证

由上,\(H\) 的陪集将 \(G\) 划分为 \([G : H]\) 个等价类,每个等价类均含有 \(|H|\) 个元素,明所欲证

由拉格朗日定理可立即得到:群中任意一个元素的阶,一定整除群的阶

共轭 (conjugate) 与正规子群 (normal subgroup)

如果存在 \(g \in H\) 使得 \(b = g ^ {-1} a g\),则称群的两个元素 \(a\)\(b\) 是共轭的,这是一个等价关系,其等价类称为共轭类 (conjugacy classes)

正规子群是在共轭变换下不变的子群,如果 \(\forall g \in G, h \in H, g ^ {-1} h g \in H\),也即满足 \(g ^ {-1}Hg = H\)\(gH = Hg\),则 \(H\)\(G\) 的正规子群,记作 \(H \trianglelefteq G\),若还有 \(H \neq G\),则称 \(H\)\(G\) 的真正规子群 (proper normal subgroup),记作 \(H \triangleleft G\)

容易发现,阿贝尔群的子群仍是阿贝尔群,故显然有 \(g ^ {-1} h g = h \in H\),即阿贝尔群的每个子群都是正规子群

商群 (quotient group)

商群是通过使用保留一些群结构的等价关系聚合更大群的相似元素获得的群

给定正规子群 \(N\),商群定义为 \(G / N = \{gN \mid g \in G\}\) 与二元运算 \(\psi(aN, bN) = (ab)N\)

由于 \(a, b \in G\),故 \(ab \in G\),故 \((ab)N\) 也是一个陪集,这验证了封闭性

容易通过群的封闭性与消去律推得,对于 \(n \in N\),有 \(nN = Nn = N\)

\(\forall n_1, n_2 \in N\),由商群乘法定义有 \((g_1g_2)N = \psi(g_1N, g_2N) = \psi(g_1n_1N, g_2n_2N) = (g_1n_1g_2n_2)N\)

所以要求 \(N\) 是正规子群,才有 \(\exists g_2n_3 = n_1g_2\),故 \((g_1n_1g_2n_2)N = (g_1g_2)(n_3n_2N) = (g_1g_2)N\),运算合法

而结合律显然成立,单位元为 \(eN\)\(gN\) 的逆元为 \(g ^ {-1}N\),故其构成一个群

群同态 (homomorphism)

群同态是保持群结构的函数,可用于关联两个群

从群 \((G, \cdot)\) 到群 \((H, *)\) 的同态是一个函数 \(\varphi: G \to H\) 使得 \(\forall a, b \in G, \varphi(a \cdot b) = \varphi(a) * \varphi(b)\)

根据 \(\varphi\) 是否是单射、满射、双射,可以定义同态映射是否是单同态 (monomorphism)、满同态 (epimorphism) 和同构 (isomorphic),单同态又称为嵌入 (embedding),记 \(G\)\(H\) 同构为 \(G \cong H\)

记所有 \(G\)\(H\) 的同态组成的集合为 \(\operatorname{Hom}(G, H)\)

\(G\)\(H\) 的同态数为 \(\operatorname{homo}(G, H)\),单同态数为 \(\operatorname{mono}(G, H)\)

群到自身的同态叫做群的自同态 (endomorphism),自同态的集合记作 \(\operatorname{End}(G)\),到自身的同构叫做群的自同构 (automorphism),自同构的集合记作 \(\operatorname{Aut}(G)\)

可以知道,\(\varphi(e_G) = e_H\),有证明 \(\varphi(a) = \varphi(ae_G) = \varphi(a) \varphi(e_G) \implies \varphi(e_G) = e_H\)

从而,容易说明 \(\varphi(G) \leq H\)

核 (kernel) 与像 (image)

在线性代数中,将矩阵乘法视为函数 \(f(x) = Ax\),那么 \(f(x) = 0\) 的解组成的空间称为零空间 (nullspace),\(f(x)\) 的所有取值组成的空间即为 \(A\) 的所有列向量所张成的空间,称为列空间 (column space),这两者就分别称为核与像

而在群论中,则有如下定义

\[\begin{gathered} \ker \varphi := \{x \in G | \varphi(x) = e_H\} = \varphi ^ {-1}(e_H) \\ \operatorname{im} \varphi := \{\varphi(x) | x \in G\} = \varphi(G) \end{gathered}\]

这带来了一个判别单同态的常用方法,\(\varphi\) 为单同态当且仅当 \(\ker \varphi = {e_G}\),证明如下

\[\varphi(g_1) = \varphi(g_2) \iff \varphi(g_1g_2 ^ {-1}) = \varphi(e_G) = e_H \iff \{e_G, g_1g_2 ^ {-1}\} \in \ker \varphi \]

容易发现,\(\ker \varphi \trianglelefteq G\),有如下证明

\[\forall g \in G, h \in \ker \varphi, \varphi(g ^ {-1}hg) = \varphi(g ^ {-1}) \varphi(h) \varphi(g) = \varphi(g ^ {-1}) \varphi(g) = \varphi(e_G) \in \ker \varphi \implies g ^ {-1} \ker \varphi g = \ker \varphi \]

同时,由商群构造可得 \(\forall N \trianglelefteq G, \exists \varphi, \ker \varphi = N\)

群同构第一定理

给定 \(G\)\(H\) 两个群,\(\varphi \in \operatorname{Hom}(G, H)\),则有群同构 \(G / \ker \varphi \cong \operatorname{im} \varphi\)

证明:构造 \(\phi: g \ker \varphi \mapsto \varphi(g)\),这将商群里的陪集映射成代表元的像

\(g_1 \ker \varphi = g_2 \ker \varphi \iff g_2 ^ {-1} g_1 \in \ker \varphi \iff \varphi(g_2 ^ {-1} g_1) = e_H \iff \varphi(g_1) = \varphi(g_2)\) 证明了 \(\phi\) 的合法性与其是单射

\(\phi(g_1 \ker \varphi)\phi(g_2 \ker \varphi) = \varphi(g_1)\varphi(g_2) = \varphi(g_1 g_2) = \phi(g_1 g_2 \ker \varphi) = \phi(g_1 \ker \varphi g_2 \ker \varphi)\) 证明了 \(\phi\) 是同态

而显然 \(\phi\) 是满射,故 \(\phi\) 就是所要求的同构

同时对于所有 \(N \trianglelefteq G, \phi: G / N \to H\),有 \(\varphi: g \mapsto \phi(gN)\) 为同态,故 \(G \to H\) 的每一个同态都对应到 \(G / N \to H\) 的一个单同态

群同构第二定理

给定群 \(G\)\(S \leq G, N \trianglelefteq G\),则有群同构 \((SN) / N \cong S / (S \cap N)\),其中 \(SN := \{sn | s \in S, n \in N\}\)

事实上,\(N\) 不一定需要是正规子群,只需要 \(S\) 中的元素满足 \(sN = Ns\) 即可,此时 \(S \cap N\) ,仍是 \(S\) 的正规子群

证明:同第一定理,构造 \(\varphi: s \mapsto sN \in \operatorname{Hom}(S, SN / N)\),由正规性可以引入商群的运算 \(\psi\)

则由 \(snN = sN\) 可得,\(\varphi\) 是一个满射,而事实上,由 \(n_1sN = sn_2N = sN\) 可知,定理中 \(SN\) 也可为 \(NS\)

最终由 \(\ker \varphi = \{s \in S | \varphi(s) = sN = N\} = \{s \in S | s \in N\} = S \cap N\),由第一定理可得

\[S / (S \cap N) = S / \ker \varphi \cong \operatorname{im} \varphi = SN / N \]

群同构第三定理

给定群 \(G\)\(N \trianglelefteq G\)

\(N \leq K \leq G\),则 \(K / N \leq G / N\),对于所有 \(H \leq G / N\),存在 \(N \leq K \leq G\) 使得 \(H = K / N\)

\(N \leq K \trianglelefteq G\),则 \(K / N \trianglelefteq G / N\),对于所有 \(H \trianglelefteq G / N\),存在 \(N \leq K \trianglelefteq G\) 使得 \(H = K / N\)

\(N \leq K \trianglelefteq G\) 则有群同构 \((G / N) / (K / N) \cong G / K\)

证明:前四者此处略去不证,对于最后一条,构造 \(\varphi: gN \mapsto gK \in \operatorname{Hom}(G / N, G / K)\),由正规性可以引入商群的运算 \(\psi\)

\(g_1N = g_2N \implies g_1 ^ {-1}g_2 \in N \leq H \implies g_1H = g_2H\),可知定义合法

显然有 \(\varphi(\psi(g_1N, g_2N)) = \varphi(g_1g_2N) = g_1g_2H = \psi(g_1H, g_2H) = \psi(\varphi(g_1N), \varphi(g_2N))\),可知 \(\varphi\) 是同态,而其显然是满射

最终由 \(\ker \varphi = \{gN | g \in G, \varphi(gN) = gK = K\} = \{gN | g \in K\} = N / K\),由第一定理可得

\[(G / N) / (K / N) = (G / N) / \ker \varphi \cong \operatorname{im} \varphi = G / K \]

群的主要类别

群的表示

群可以用生成元和关系来表示:\(G = \langle S | R \rangle\)

对称群 (symmetric group) 与交错群 (alternating group)

\(n\) 阶对称群记作 \(S_n\)\(n\) 阶交错群记作 \(A_n\),分别是由置换与偶置换组成的群

循环群 (cyclic group)

\(n\) 阶循环群记作 \(C_n\),是最简单的群,群 \(G\) 中任意一个元素 \(a\) 都可以表示为 \(a = g ^ k\),称 g 为群 G 的生成元,其也可表为 \(\langle a | a ^ n = e\rangle\)

\(2n\) 阶二面体群 (dihedral group)

\(2n\) 阶二面体群 \(D_{2n}\) 是一个平面正 \(n\) 边形的对称群,也可以记成 \(\langle a, b | a ^ n = b ^ 2 = (ab) ^ 2 = e\rangle\)

一般线性群 (general linear group)

\(n\) 次一般线性群 \(GL_n\)\(n \times n\) 可逆矩阵组成的群,带有矩阵乘法作为群运算,若矩阵元素来自域 \(\mathbb F\),则写为 \(GL_n(\mathbb F)\)

Light 算法

有一个经典的问题,给定一张乘法表,如何检验其中的元素是否构成一个群?

对于封闭性,直接检验即可

寻找 \(g \circ g = g\)\(g\),可得 \(g = e\),判断 \(eg = g\) 即可判定左单位元

对于每个 \(g\),寻找满足 \(g ^ {-1}g = e\) 的左逆元,接下来就只剩下结合律了

如果直接按照定义检验,我们需要枚举元素 \(f, g, h\),复杂度 \(O(n ^ 3)\)

但对于群,可以得到一个在 \(O(n ^ 2 \log n)\) 时间内的算法

首先,对于任意 \(n\) 阶有限群 \(G\),存在 \(S \subseteq G\),使得\(|S| \leq \log_2 n, \langle S \rangle = G\)

只需构造 \(S ^ * = S \cup \{g\}, g \in G \setminus \langle S \rangle\),则有 \(|\langle S ^ * \rangle| \geq 2|\langle S \rangle|\),直到 \(\langle S \rangle = G\) 即可,若构造过程中并没有成倍增长,则可以直接退出

并且若 \(a, b\) 满足 \((ga)h = g(ah), (gb)h = g(bh)\),则 \((g(ab))h = ((ga)b)h = (ga)(bh) = g(a(bh)) = g((ab)h)\),可知 \(ab\) 也满足,故只需对于选出的 \(S\) 判定即可,复杂度 \(O(n ^ 2 \log n)\)

三、置换群

置换

一个有限集合 \(S\) 到自身的双射称为 \(S\) 的一个置换,集合 \(S = \{1, 2, \cdots, n\}\) 上的置换 \(\sigma\) 可以表示为

\[f = \begin{pmatrix} 1, 2, \cdots, n \\ \sigma_1, \sigma_2, \cdots, \sigma_n \end{pmatrix}\]

意为将 \(i\) 映射为 \(\sigma_i\),其中 \(\sigma\)\(1, 2, \dots, n\) 的一个排列,显然 \(S\) 上所有置换的数量为 \(n!\)

对于两个置换 \(f = \begin{pmatrix}1, 2, \cdots, n \\ \sigma_1, \sigma_2, \cdots, \sigma_n \end{pmatrix}\)\(g = \begin{pmatrix} 1, 2, \cdots, n \\ \tau_1, \tau_2, \cdots, \tau_n\end{pmatrix}\)\(f\)\(g\) 的乘积记为 \(f \circ g = \begin{pmatrix} 1, 2, \cdots, n \\ (\tau \circ \sigma)_1, (\tau \circ \sigma)_2, \cdots, (\tau \circ \sigma)_n \end{pmatrix}\)

简单来说就是先后经过 \(\sigma\) 的映射,再经过 \(\tau\) 的映射

排列

\(I = \{1, 2, \cdots, n\}\) 是前 \(n\) 个正整数构成的集合,\(\sigma\)\(I\) 的一个置换,记 \(\sigma(1) = i_1 \quad \sigma(2) = i_2 \quad \cdots \quad \sigma(n) = i_n\)

于是 \(i_1, i_2, \cdots, i_n\) 是由 \(1, 2, \cdots, n\) 组成的一个有序组,称为 \(1, 2, \cdots, n\) 的一个排列,显然前 \(n\) 个正整数 \(1, 2, \cdots, n\) 的不同排列共有 \(n!\)

逆序和逆序数

在一个排列中,如果某一个较大的数排在某一个较小的数前面,就说这两个数构成一个逆序,在一个排列里出现的反序的总个数,叫做这个排列的逆序数

定义排列的奇偶性与排列的逆序数的奇偶性相同,置换的奇偶性与对应的排列 \(p\) 的奇偶性相等

对换与循环

若一个置换对于 \(a_1, a_2, \cdots, a_m\),有 \(\sigma_{a_i} = a_{i + 1} (1 \le i < m), \sigma_{a_m} = a_1\),则称其为一个 \(m\) 循环, 记为 \((a_1, a_2, \cdots a_m)\)

如果两个轮换满足 \(\{a_1, a_2, \cdots, a_m\} \cap \{b_1, b_2, \cdots, b_k\} = \varnothing\),则称二者不相交,\(2\) 轮换称为对换

容易发现,对换 \((i, i + 1)\) 可以改变置换的奇偶性,故 \((i, j) = (i, i + 1)(i + 1, i + 2)\cdots(j - 2, j - 1)(j - 1, j)(j - 2, j - 1)\cdots(i + 1, i + 2)(i, i + 1)\) 也可以改变置换的奇偶性

故置换的奇偶性也为分解成的对换数量的奇偶性,置换的复合的奇偶性满足模 \(2\) 加法

置换群、对称群与交错群

\(S = \{1, 2, \cdots, n\}\) 上的所有置换构成的群为 \(n\) 级对称群,称为 \(S_n\)

同时,其中所有偶置换在映射乘法下同样组成一个群,称为 \(n\) 级交错群,记为 \(A_n\)

显然,当 \(n \ge 2\) 时,可以通过交换 \(\sigma_1\)\(\sigma_2\) 来在奇偶置换间构造双射,故 $S_n = $

我们称 \(S_n\) 的子群为 \(n\) 次置换群

\(S_n\)\(A_n\) 的置换分解与生成元

显然,\(S_n\) 中任意元素可以唯一地分解为不交循环之积

故由于 \((i_1, i_2, \cdots, i_n) = (i_1, i_n)(i_1, i_{n - 1})\cdots(i_1, i_2)\)\(S_n\) 中任意元素可以分解为对换之积

因为 \((i, j) = (1, i)(1, j)(1, i)\),故 \(S_n = \langle (1, i) \rangle\)

因为 \((1, k + 1) = (1, k)(k, k + 1)(1, k)\),故 \(S_n = \langle (i, i + 1) \rangle\)

因为 \((i, i + 1) = (1, 2, 3, \cdots, n)(i - 1, i)(1, 2, 3, \cdots, n) ^ {-1}\),故 \(S_n = \langle (1, 2), (1, 2, 3, \cdots, n) \rangle\)

因为 \((i, s)(i, t) = (i, s, t), (i, j)(s, t) = (i, j)(i, s)(s, i)(s, t) = (i, j, s)(s, i, t)\),且 \((i, j, k)\) 是偶置换,故 \(A_n = \langle (i, j, k) \rangle\)

因为 \((a, b, i)(a, b, j) = (a, j)(b, i)\),故可以构造 \((1, i)(2, j)\),且 \(\sigma(i, j, k)\sigma ^ {-1} = (\sigma ^ {-1}_i, \sigma ^ {-1}_j, \sigma ^ {-1}_k)\),依此也可知是交错群 \(A_n\) 的正规子群,若含有三轮换,则必定为 \(A_n\) 本身

\((i, j, k) = (1, i)(2, j)(i, j, k)((1, i)(2, j)) ^ {-1} = (1, 2, j)(1, 2, i)(1, 2, k)(1, 2, j)(1, 2, i)\),且 \((1, i, j) = (1, 2, j)(1, 2, i)(1, 2, j) ^ {-1}\)\((2, i, j)\) 同理,可得 \(A_n = \langle (1, 2, i) \rangle\)

\(n\) 为奇数,则 \((1, 2, i + 1) = (1, 2, \cdots, n) ^ {-1} (1, 2, i) ^ {-1} (1, 2, \cdots, n) (1, 2, 3)\),故 \(A_n = \langle (1, 2, 3), (1, 2, \cdots, n) \rangle\)

\(n\) 为偶数,则 \((1, 2, i + 1) = (1, 2, 3) (2, 3, \cdots, n) ^ {-1} (1, 2, i) ^ {-1} (2, 3, \cdots, n)\),故 \(A_n = \langle (1, 2, 3), (2, 3, \cdots, n) \rangle\)

一道例题:ABC296F

凯莱 (Cayley) 定理

Cayley 定理说明,任何有限群 \((G, \cdot)\) 都同构于一个置换群 \(H\)

事实上,这很容易构造,设群 \(G = \{g_1, g_2, g_3, \cdots, g_n\}\)

构造置换 \(f(g_k) = \left(\begin{gathered} g_1, g_2, g_3, \cdots, g_n \\ g_kg_1, g_kg_2, g_kg_3, \cdots, g_kg_n \end{gathered}\right)\) 与其构成的群 \(\displaystyle H = \{f(g_k) | 1 \le k \le n\}\) 即可

显然 \(g_kg_i\) 互异,故置换合法,且有单位元 \(f(e)\)\(f(g_k)\) 的逆元 \(f(g_k ^ {-1})\)

以此我们可以构造出以下群

一到四阶的群:\(\langle (1) \rangle, \langle (1, 2) \rangle, \langle (1, 2, 3) \rangle, \langle (1, 2, 3, 4) \rangle, \langle (1, 2), (3, 4) \rangle\)

循环群 \(C_n\)\(\langle (1, 2, 3, \cdots, n) \rangle\)

\(2n\) 阶二面体群 \(D_n\)\(\langle (1, 2, \cdots, n)(2n, 2n - 1, \cdots, n + 1), (1, n + 1)(2, n + 2)\cdots(n, 2n) \rangle\)\(\langle (1, 2, \cdots, n), (2, n)(3, n - 1)(4, n - 2)\cdots \rangle\)

四面体群:一个正四面体的对称群,可以发现可以对 \(A, B, C, D\) 四个顶点其中三个轮换,故其同构于 \(A_4\)

四、一些具有启发性的小例题

一、群 \(G\) 的元素中 \(a, b\) 满足 \(a\) 的阶是 \(7\)\(a^3 b = ba^3\),求证 \(ab = ba\)

\[\begin{aligned} a ^ 3 & = b a ^ 3 b ^ {-1} \\ (a ^ 3) ^ 5 & = (b a ^ 3 b ^ {-1}) ^ 5 \\ a ^ {15} & = b a ^ {15} b ^ {-1} \\ a & = b a b ^ {-1} \\ a b & = b a \end{aligned}\]

这本质是因为 \(\gcd(3, 7) = 1\)

二、群 \(G\) 的元素中 \(a, b\),求证 \(ab, ba\) 同阶

\(\operatorname{ord}(ab) = k\),那么有 \((ab) ^ k = e \implies a (ba) ^ {k-1} b = e \implies a (ba) ^ {k-1} = b ^ {-1} \implies (ba) ^ k = e\)

\(\operatorname{ord}(ba) \le \operatorname{ord}(ab)\),同理有 \(\operatorname{ord}(ba) \ge \operatorname{ord}(ab)\),因此二者相等

注意到一些元素的乘积是单位元的时候可以对元素进行 cyclic shift

三、举例说明群中一些阶有限的元素的乘积有可能阶是无限的

在全体双射 \(f: \mathbb Z\rightarrow \mathbb Z\) 的复合运算构成的群中,令 \(f(n) = 1 - n, g(n) = -n\),那么 \(f, g\) 的阶都是 \(2\),但 \(f(g(n)) = n + 1\),阶是无限的

四、举例说明群 \(G\) 的阶不一定等于其中所有元素阶的最大值(或 \(\operatorname{lcm}\)

二面体群 \(D_4\)(相当于群 \((\{0, 1, 2, 3\}, \oplus)\) 的阶是 \(4\),但是除了 \(e\) 的阶为 \(1\),其他元素的阶都是 \(2\)

五、举例说明如果群 \(G\) 中存在两个元素 \(x_1\)\(x_2\) 的阶是 \(d_1\)\(d_2\),那么 \(G\) 中不一定存在阶为 \(d = \operatorname{lcm}(d_1, d_2)\) 的元素

对称群 \(S_3\)(相当于 \(X = \{1, 2, 3\}\) 的置换群)中存在阶为 \(2\)\(3\) 的元素,却不存在阶为 \(6\) 的元素

六、证明 \(\langle a, b | a ^ 2 = b ^ 3 = (ab) ^ 4 = e \rangle \cong S_{4}\)

考虑 \(a = (1, 2), b = (2, 3, 4)\),那么有 \(ab = (1, 3, 4, 2)\),满足上述条件

七、证明 \(\langle a, b | a ^ 2 = b ^ 3 = (ab) ^ 3 = e \rangle \cong A_{4}\)

考虑 \(a = (1, 2)(3, 4), b = (1, 2, 3)\),那么有 \(ab = (2, 4, 3)\),满足上述条件

有人拿了这个东西来出题:300iq contest1 K

五、Abel 群的性质

todo

六、Burnside 引理与 Pólya 定理

一些定义

\(G\) 是群,\(X \neq \varnothing\),若定义在 $$G \times X \to X$$ 上的二元运算 \(\cdot\) 满足:

(1) 对乘积的作用等于相继作用:\(\forall g_1, g_2 \in G, c \in X, g_2 \cdot (g_1 \cdot c) = (g_1g_2) \cdot c\)

(2) 单位元的作用:\(\forall c \in X: e \cdot c = c\)

则称二元运算 \(\cdot\)\(G\)\(X\) 上的一个作用,下文中的染色,指的即是集合 \(X\) 中的一个元素

染色 \(c\)\(G\) 中的轨道 (orbit),即 \(G\) 中所有置换作用于 \(c\) 所得的染色集合,用 \(G \cdot c = \{g \cdot c | g \in G\}\) 表示

染色 \(c\) 的稳定子群 (stabilizer),为满足 \(g \cdot c = c\) 的置换 \(g\) 构成的子群,用 \(G_c\) 表示

轨道-稳定子定理

轨道-稳定子定理,用上述定义表示就是 \(|G \cdot c||G_c| = |G|\)

首先显然有 \(G_c\)\(G\) 的子群,则由拉格朗日定理,可得 \(|G| = |G_c|[G : G_c]\)

并且存在双射 \(\varphi: g \cdot c \mapsto gG_c\),这是由于 \(f \cdot c = g \cdot c \iff f ^ {-1} \circ g \in G_c \iff g \in fG_c \iff fG_c = gG_c\) 与显然存在的逆映射,故证

Burnside 引理

Burnside 引理,即在 \(G\) 的作用下,\(X\) 中元素形成的不同轨道数目,等于 \(G\) 中所有置换的不动点个数的平均值

如果用 \(X / G\) 表示不同轨道的集合(注意与商群区分),\(X ^ g\) 表示置换 \(g\) 的不动点集合,则 Burnside 引理可以写成 \(\displaystyle |G||X / G| = \sum_{g \in G} |X ^ g|\)

可以通过算两次 \(\displaystyle \sum_{g \in G}\sum_{c \in X} [g \cdot c = c]\) 来证明

当外层枚举 \(g\) 时,就得到了 \(\displaystyle \sum_{g \in G}|X ^ g|\)

当外层枚举 \(c\) 时,就得到 \(\displaystyle \sum_{c \in X}|G_c| = |G|\sum_{c \in X}\frac{1}{|G\cdot c|} = |G||X / G|\)

循环指标 (cycle index)

对于 \(n\) 元置换 \(g\),设其分解为的不交循环之积为 \((a_{11}, a_{12}, \cdots, a_{1L_1})(a_{21}, a_{22}, \cdots, a_{2L_2})\cdots(a_{x1}, a_{x2}, \cdots, a_{xL_x})\)

设这些循环中有 \(\#i\) 个循环大小为 \(i\),则定义 \(g\) 的循环指标为 \(t_1 ^ {\#1} t_2 ^ {\#2} \cdots t_n ^ {\#n}\),其中 \(t_i\) 为形式变元

如:置换 \((1, 4, 2, 5, 3)\) 的循环指标为 \(t_5\),置换 \((1, 4)(2, 5)(3, 6, 7)\) 的循环指标为 \(t_2 ^ 2 t_3\)

定义一个置换群 \(G\) 的循环指标,为群中所有置换的循环指标的平均值,记作 \(Z_G(t_1, t_2, \cdots, t_n)\)

Pólya 定理

\(X\) 为所有从 \(1, 2, \cdots, n\)\(1, 2, \cdots, m\) 的映射,\(g \cdot c := c \circ g\),则有:

\(|X ^ g| = m ^ {\#1 + \#2 + \cdots \#n}\),故有 \(\displaystyle |X / G| = Z_G(m, m, \cdots, m)\)

假设存在权值 \(w(c)\),一个映射的权值为 \(\displaystyle \sum_{i = 1} ^ n w(c_i)\),设生成函数 \(\displaystyle f(x) = \sum_c x^{w(c)}\),有:

\[|X / G|(x) = Z_G(f(x), f(x ^ 2), \cdots, f(x ^ n)) \]

其中 \(n\) 次项是权值和为 \(n\) 的不同轨道数。

例题

Luogu P4128 [SHOI2006] 有色图

听说有色图?来了来了!色图在哪里啊?

那么我们考虑一个循环指标 \(t_1 ^ {\#1} t_2 ^ {\#2} \cdots t_n ^ {\#n}\) 的不动点个数,显然它的不动点个数为 \(m\) 的在这个置换下边的等价类个数次方

考虑两类边,第一类是端点在同一循环中的边,第二类是端点在不同循环中的边

对于前者,容易得到两条边处于不同等价类当且仅当它们长度不同,有 \(\displaystyle\left\lfloor\frac{i}{2}\right\rfloor\) 种边的等价类

对于后者,一条边,在这个置换的重复作用下经过 \(\mathrm{lcm}(i, j)\) 次操作会回到自身,等价类的个数为 \(\displaystyle\frac{ij}{\mathrm{lcm}(i, j)} = \gcd(i, j)\)

接下来需要对所有循环指标统计,这只需要于枚举 \(n\) 的整数分拆即可

对于一个循环指标 \(t_1 ^ {\#1} t_2 ^ {\#2} \cdots t_n ^ {\#n}\),先对于 \(1\)\(n\) 的每个点,分配它在第几个循环中,有 \(\displaystyle \frac{n!}{\displaystyle \prod i! ^ {\#i}}\)

而对于每一个置换,分配它内部的顺序,这相当于一个圆排列,有 \(\displaystyle \prod (i - 1)! ^ {\#i}\) 种,并且发现长度相同的循环之间的顺序不影响,要除掉 \(\#1! \#2! \cdots \#n!\)

故最终的式子是

\[\sum \frac{m ^ {\displaystyle \sum \#i\left\lfloor\frac{i}{2}\right\rfloor + \sum \#i\#j\gcd(i, j)}}{\displaystyle \prod i ^ {\#i} \#i!} \]

而拆分数有估计 \(p_n \le \exp \sqrt{\dfrac{20n}{3}}\),可以通过此题

Luogu P4980 模板 Pólya 定理

Luogu P4727 HNOI2009 图的同构计数

Luogu P4916 MtOI2018 魔力环

Luogu P2561 AHOI2002 黑白瓷砖

Luogu P1446 HNOI2008 Cards

七、群论的一些计算问题

Schreier–Sims 算法 (降群法)

简介

Schreier–Sims 算法可以计算 \(G \leq S_n\) 的基 (base) 和强生成集 (strong generating set)

对于元素互异的序列 \(b_1, b_2, \cdots, b_m\),记 \(G ^ {(i)} = G_{b_1, \cdots, b_i}\),故有 \(G ^ {(0)} = G\)

称序列 \(b_i\) 为基,当且仅当 \(G ^ {(m)} = e\),此时若对于生成集 \(\langle S \rangle = G\),有 \(G ^ {(i)} = \langle S \cap G ^ {(i)} \rangle\),则称 \(S\)\(G\) 对于 \(b_i\) 的强生成集

也就是说,Schreier–Sims 算法求出了子群链 \(G ^ {(0)} \geq G ^ {(1)} \geq \cdots \geq G ^ {(m)} = \{e\}\),依此可以有许多应用

事实上,Schreier–Sims 算法除了以下讲的还有许多优化,可自行查找

截面 (transversal)

考虑 \(G ^ {(i)}\) 依然是一个很大的群,但其的陪集个数 \([G ^ {(i)} : G ^ {(i + 1)}]\) 并不是很多,我们考虑对每个陪集选取一个代表元素

对于群 \(G\) 和子群 \(H \leq G\),设 \(H\) 导出的左陪集集合为 \(g_iH\),则包含单位元的集合 \(R = \{r_i | r_i \in g_iH\}\) 称为 H 的一个左截面,同理可以定义右截面,但此处不考虑

易知对于 \(i \neq j\)\(r_iH \neq r_jH\),即 \(r_i ^ {-1}r_j \not \in H\)

对每一个陪集,取代表元素 \(u_i\),对于 \(g \in u_iH\),称 \(u_i\)\(g\) 的标准置换,记作 \(\operatorname{norm} g\)

Schreier 引理

对于群 \(G = \langle S \rangle\)\(H \leq G\),我们想要求一个可以接受的 \(\langle S' \rangle = H\),从而更好的维护子群链

\(R\)\(H\) 的一个左截面,那么 \(S' = \{(\operatorname{norm}(sr)) ^ {−1}(sr) | r \in R, s \in S\}\) 满足要求

显然 \((sr) \in \operatorname{norm}(sr) H \implies (\operatorname{norm}(sr)) ^ {−1}(sr) \in H \implies \langle S' \rangle \subseteq H\),下证 \(H \subseteq \langle S' \rangle\)

由于 \(e \in R\),因此 \(\forall h \in H, h = s_1 s_2 \cdots s_k r\),其中 \(r \in R, s_i \in S\)

\(k = 0\) 时,\(h = r \in H \cap R = \{e\} \leq \langle S' \rangle\)

并且,\(h = s_1 s_2 \cdots s_k r = s_1 \cdots s_{k - 1} \operatorname{norm}(s_k r) (\operatorname{norm}(s_k r)) ^ {-1} (s_k r)\),由于 \((\operatorname{norm}(s_k r)) ^ {-1} (s_k r) \in \langle S' \rangle, \operatorname{norm}(s_k r) \in R\),故可以进行归纳从而证明

算法流程

算法使用了增量构造法,即逐渐向 \(S\) 中添加元素,然后对子群链中的每个子群进行维护

初始时,\(S = \varnothing, G = \{e\}\),记子群链的下一个元素为 \(H\),固定了 \(u\),考虑向 \(S\) 中添加一个新元素 \(g\)

第一步,我们需要查询 \(g\) 是否已经在 \(\langle S \rangle\) 中,首先寻找 \(gr \in H \implies r \in g ^ {-1}H\),即 \(\operatorname{norm}(g ^ {-1})\)

注意到置换群的特殊性,有 \((rh) ^ {-1} \circ u = (h ^ {-1} r ^ {-1}) \circ u = r ^ {-1} \circ u\),从而可以唯一确定 \(gH = r_iH\),取其为 \(\operatorname{norm}(g)\) 即可

故求 \(\operatorname{norm}(g ^ {-1})\),可以记录 \(x_{r_i ^ {-1} \circ u} = i\)\(x_{g \circ u}\) 即为所求

若不存在,则显然 \(g \not \in G\),否则继续查询 \(g \in G \iff gr \in H\)

\(g\) 不在 \(\langle S \rangle\) 中,则将其加入 \(S\),接下来由于 \(R\) 中每个元素记录的是 \(u\) 的不同的原像,因此需要知道 \(u\) 多了哪些原像

设原先 \(u\) 的原像集合为 \(A\),考虑置换 \(rg\),可以得到 \(\forall p \in A, r \circ u = p \implies (rg) \circ u = g \circ p\) 也成了 \(u\) 的像,然后我们只需要枚举 \(S\) 中元素继续搜索即可,这事实上是一个不断合并轨道的过程

由 Schreier 引理,设 \(R\) 中新增了元素 \(r\),只需枚举 \(S\) 中所有的元素 \(s\),向 \(H\) 中添加 \((\operatorname{norm}(sr)) ^ {−1}(sr)\),由于之前是先在 \(S\) 中增加 \(g\),因此也需要枚举 \(r \in R\)\((\operatorname{norm}(gr)) ^ {−1}(gr)\)

事实上,在 \(S\) 中增加 \(g\) 时,枚举 \(\pi = gr\),若 \(\operatorname{norm}(\pi)\) 不存在,则将其加入 \(R\),并继续搜索 \(\pi' = s\pi\),否则直接向 \(H\) 中尝试添加 \((\operatorname{norm} \pi) ^ {−1} \pi\) 即可

Minkwitz 算法

设群 \(G = \langle S \rangle\),Minkwitz 算法可以将群中的元素 \(g\) 分解成 \(s_1 s_2 \cdots s_k\) 的形式,其中 \(s_i \in S \cap S ^ {-1}\)

Schreier–Sims 算法中的构造 \((\operatorname{norm}(sr)) ^ {−1}(sr)\) 会使分解的长度指数级增长,所以 Minkwitz 算法通过广搜结合迭代加深的方法加以抑制

记录 \(V_{i}(j) \in G ^ {(i - 1)}\) 表示固定前 \(i - 1\) 个数,将 \(j\) 置换到 \(i\) 处的置换的分解,初始化 \(V_{i}(i) = e\),通过 Schreier–Sims 算法我们已知了哪些 \(V_{i}(j)\) 可能有值

我们考虑依次搜索长度为 \(1, 2, \cdots\) 的置换分解 \(w\),若 \(V_{i}(j)\) 还未填满,则令 \(l \gets \dfrac{5}{4}l\),接下来从 \(i = 0\) 开始搜索

\(V_i(w \circ i)\) 不存在,就直接令 \(V_i(w \circ i) = w ^ {-1}\),并且对 \(w ^ {-1}\) 进行确认:若 \(V_i(w ^ {-1} \circ i)\) 不存在或长度比 \(w\) 长,就令其为 \(w\)

否则,若 \(w\) 的长度比 \(V_i(w \circ i)\) 长,则直接结束搜索

\(w\) 长度更短,若 \(w V_i(w \circ i)\) 长度低于 \(l\),则对于 \(i + 1\) 搜索之,再更新 \(V_i(w \circ i)\),并且对 \(w ^ {-1}\) 进行确认

Minkwitz 算法还有一些优化,具体可参见论文 An Algorithm for Solving the Factorization Problem in Permutation Groups

针对魔方群的一份代码

#include <bits/stdc++.h>

namespace GroupTheory {

using std::vector;
using std::array;
using std::string;

template<int _N> struct Permutation {
    static constexpr int N = _N;
    array<int, N> perm;
    static const Permutation<N> maps(const array<int, N> &p) { return Permutation<N> { p }; }
    static const Permutation<N> cycle(const vector<vector<int>> &cyc) {
        Permutation<N> res;
        std::iota(res.perm.begin(), res.perm.end(), 0);
        for(const vector<int> &v: cyc)
            for(int i = 0; i < (int)v.size(); ++i)
                res.perm[v[i]] = v[i + 1 == (int)v.size() ? 0 : i + 1];
        return res;
    }
    static const Permutation<N> identity() {
        array<int, N> arr;
        std::iota(arr.begin(), arr.end(), 0);
        return Permutation<N>::maps(arr);
    }
    const string maps() const {
        string res;
        for(int i = 0; i < N; ++i) {
            res += std::to_string(perm[i]);
            if(i + 1 != N) res += " ";
        }
        return res;
    }
    const string cycle() const {
        std::bitset<N> vis;
        vector<vector<int>> cyc;
        for(int i = 0; i < N; ++i) {
            if(vis[i] || perm[i] == i) continue;
            vector<int> c;
            for(; !vis[i]; i = perm[i])
                vis[i] = 1, c.push_back(i);
            cyc.push_back(c);
        }
        if(!cyc.size()) return string("e");
        string res, tmp;
        for(const vector<int> &v: cyc) {
            for(int i = 0; i < (int)v.size(); ++i) {
                tmp += std::to_string(v[i]);
                if(i + 1 != (int)v.size()) tmp += " ";
            }
            res += "(" + tmp + ")", tmp.clear();
        }
        return res;
    }
    const Permutation<N> operator~() const {
        Permutation<N> res;
        for(int i = 0; i < N; ++i)
            res.perm[perm[i]] = i;
        return res;
    }
    const Permutation operator*(const Permutation &o) const {
        Permutation<N> res;
        for(int i = 0; i < N; ++i)
            res.perm[i] = o.perm[perm[i]];
        return res;
    }
    const int operator*(const int o) const { return perm[o]; }
};

template<typename Perm> struct PermGroup {
    static constexpr int N = Perm::N;
    int u; vector<Perm> S;
    array<Perm *, N> R;
    PermGroup<Perm> *nxt;
    PermGroup(int n = N, int i = 0) {
        if(i == n - 1) nxt = nullptr;
        else nxt = new PermGroup<Perm>(n, i + 1);
        for(int i = 0; i < N; ++i) R[i] = nullptr;
        u = i, R[u] = new Perm(Perm::identity());
    }
    ~PermGroup() { if(nxt != nullptr) delete nxt; }
    bool test(const Perm &g) {
        const int pos = g * u;
        if(R[pos] == nullptr) return false;
        if(nxt == nullptr) return true;
        return nxt->test(g * *R[pos]);
    }
    void updGenerator(const Perm &g) {
        if(test(g)) return;
        S.push_back(g);
        for(int i = 0; i < N; ++i)
            if(R[i] != nullptr) updTransversal(g * *R[i]);
    }
    void updTransversal(const Perm &g) {
        const int pos = (~g) * u;
        if(R[pos] == nullptr) {
            R[pos] = new Perm(g);
            for(const Perm &s: S) updTransversal(s * g);
        } else if(nxt != nullptr)
            nxt->updGenerator((~*R[pos]) * g);
    }
};

template<typename Perm, typename Word, typename Generator> struct PermTable {
    static constexpr int N = Perm::N;
    array<array<Word *, N>, N> V;
    array<int, N> lst; int full;
    PermTable() {}
    PermTable(const PermGroup<Perm> &pg) {
        const PermGroup<Perm>* nw = &pg;
        for(int i = 0; i < N; ++i) {
            int cnt = 0;
            for(int j = 0; j < N; ++j)
                if(nw->R[j] != nullptr) ++cnt;
            nw = nw->nxt, lst[i] = cnt;
        }
        full = 0;
        for(int i = 0; i < N; ++i) {
            for(int j = 0; j < N; ++j) V[i][j] = nullptr;
            V[i][i] = new Word(Word::identity());
            if(!--lst[i]) ++full;
        }
    }
    void step(int i, const Word &g, Word &r) {
        const int pos = g.perm * i;
        if(V[i][pos] != nullptr) {
            r = g * *V[i][pos];
            if(g.size() > V[i][pos]->size()) return;
        } else r = Word::identity(), full += !(--lst[i]);
        V[i][pos] = new Word(~g);
        const int posrev = (~g.perm) * i;
        if(V[i][posrev] != nullptr) {
            if(g.size() > V[i][posrev]->size()) return;
        } else full += !(--lst[i]);
        V[i][posrev] = new Word(g);
    }
    void round(int mxlen, const Word g) {
        Word t = g, r;
        for(int i = 0; i < N; ++i) {
            step(i, t, r), t = r;
            if(!t.size() || t.size() > mxlen) return;
        }
    }
    void getWord(int n) {
        Generator gen;
        for(int i = 0, mxlen = 10; ; ++i) {
            round(mxlen, gen.next());
            if(full != N) mxlen = mxlen * 5 / 4;
            if(full == N && i >= n) return;
        }
    }
};

} // namespace GroupTheory

namespace Cube {

using std::vector;
using Trans = GroupTheory::Permutation<48>;
using Cube = Trans;

/*
           +----------+
           | 00 01 02 |
           | 03 UP 04 |
           | 05 06 07 |
+----------+----------+----------+----------+
| 08 09 10 | 16 17 18 | 24 25 26 | 32 33 34 |
| 11 LT 12 | 19 FT 20 | 27 RT 28 | 35 BK 36 |
| 13 14 15 | 21 22 23 | 29 30 31 | 37 38 39 |
+----------+----------+----------+----------+
           | 40 41 42 |
           | 43 DN 44 |
           | 45 46 47 |
           +----------+
*/

const Trans U = Trans::cycle({ { 0, 2, 7, 5 }, { 1, 4, 6, 3 },
        { 8, 32, 24, 16 }, { 9, 33, 25, 17 }, { 10, 34, 26, 18 } });
const Trans D = Trans::cycle({ { 40, 42, 47, 45 }, { 41, 44, 46, 43 },
        { 13, 21, 29, 37 }, { 14, 22, 30, 38 }, { 15, 23, 31, 39 } });
const Trans L = Trans::cycle({ { 8, 10, 15, 13 }, { 9, 12, 14, 11 },
        { 0, 16, 40, 39 }, { 3, 19, 43, 36 }, { 5, 21, 45, 34 } });
const Trans R = Trans::cycle({ { 24, 26, 31, 29 }, { 25, 28, 30, 27 },
        { 2, 37, 42, 18 }, { 4, 35, 44, 20 }, { 7, 32, 47, 23 } });
const Trans F = Trans::cycle({ { 16, 18, 23, 21 }, { 17, 20, 22, 19 },
        { 5, 24, 42, 15 }, { 6, 27, 41, 12 }, { 7, 29, 40, 10 } });
const Trans B = Trans::cycle({ { 32, 34, 39, 37 }, { 33, 36, 38, 35 },
        { 2, 8, 45, 31 }, { 1, 11, 46, 28 }, { 0, 13, 47, 26 } });

struct TransList {
    static constexpr int N = 48;
    enum class RotateBasic { U, D, L, R, F, B };
    struct Rotate { RotateBasic basic; int rev; };
    Trans perm; vector<Rotate> optList;
    static const TransList identity() { return TransList({ Trans::identity() }); }
    static Trans rotateToTrans(const Rotate r) {
        switch(r.basic) {
            case RotateBasic::U: return r.rev ? ~U : U;
            case RotateBasic::D: return r.rev ? ~D : D;
            case RotateBasic::L: return r.rev ? ~L : L;
            case RotateBasic::R: return r.rev ? ~R : R;
            case RotateBasic::F: return r.rev ? ~F : F;
            case RotateBasic::B: return r.rev ? ~B : B;
        }
        return Trans::identity();
    }
    static int nextRotate(Rotate &r) {
        switch(r.basic) {
            case RotateBasic::U: r.basic = RotateBasic::D; break;
            case RotateBasic::D: r.basic = RotateBasic::L; break;
            case RotateBasic::L: r.basic = RotateBasic::R; break;
            case RotateBasic::R: r.basic = RotateBasic::F; break;
            case RotateBasic::F: r.basic = RotateBasic::B; break;
            case RotateBasic::B: r.basic = RotateBasic::U, r.rev ^= 1; break;
        }
        return r.basic != RotateBasic::U || r.rev;
    }
    int size() const { return optList.size(); }
    const TransList operator~() const {
        TransList res = *this;
        std::reverse(res.optList.begin(), res.optList.end());
        for(Rotate &r: res.optList) r.rev ^= 1;
        return res.perm = ~res.perm, res;
    }
    const TransList operator*(const TransList &o) const {
        TransList res = *this;
        res.perm = res.perm * o.perm;
        res.optList.insert(res.optList.end(), o.optList.begin(), o.optList.end());
        return res;
    }
    const int operator*(const int o) const { return perm.perm[o]; }
    Rotate pop() {
        Rotate t = *optList.rbegin();
        optList.pop_back();
        perm = perm * ~rotateToTrans(t);
        return t;
    }
};

struct TransGen {
    int mxsz; TransList nw;
    TransGen() { mxsz = 0, nw = TransList::identity(); }
    static constexpr TransList::Rotate bg = TransList::Rotate({ TransList::RotateBasic::U, 0 });
    TransList rotateToTransList(const TransList::Rotate r = bg)
        { return TransList({ TransList::rotateToTrans(r), { r } }); }
    void rebuild() { while(nw.size() < mxsz) nw = nw * rotateToTransList(); }
    TransList next() {
        while(nw.size()) {
            TransList::Rotate r = nw.pop();
            if(TransList::nextRotate(r))
                { nw = nw * rotateToTransList(r); break; }
        }
        if(!nw.size()) ++mxsz;
        return rebuild(), nw;
    }
};

struct Solver {
    GroupTheory::PermGroup<Trans> pg;
    GroupTheory::PermTable<Trans, TransList, TransGen> pt;
    Solver() {
        pg.updGenerator(U), pg.updGenerator(D);
        pg.updGenerator(L), pg.updGenerator(R);
        pg.updGenerator(F), pg.updGenerator(B);
        pt = GroupTheory::PermTable<Trans, TransList, TransGen>(pg);
        pt.getWord(30000);
    }
    TransList solve(Cube cb) {
        TransList res = TransList::identity();
        for(int i = 0; i < Trans::N; ++i) {
            const int pos = cb * i;
            res = res * *pt.V[i][pos];
            cb = cb * pt.V[i][pos]->perm;
        }
        return res;
    }
} solver;

} // namespace Cube

int main() {
    Cube::Cube cb = Cube::Cube::identity();
    std::string str;
    while(std::cin >> str) {
    	switch(str[0]) {
    		case 'U': cb = cb * (str.length() == 1 ? Cube::U : ~Cube::U); break;
    		case 'D': cb = cb * (str.length() == 1 ? Cube::D : ~Cube::D); break;
    		case 'L': cb = cb * (str.length() == 1 ? Cube::L : ~Cube::L); break;
    		case 'R': cb = cb * (str.length() == 1 ? Cube::R : ~Cube::R); break;
    		case 'F': cb = cb * (str.length() == 1 ? Cube::F : ~Cube::F); break;
    		case 'B': cb = cb * (str.length() == 1 ? Cube::B : ~Cube::B); break;
    	}
    }
    Cube::TransList tr = Cube::solver.solve(cb);
    for(int i = 0; i < tr.size(); ++i) {
        const Cube::TransList::Rotate r = tr.optList[i];
        switch(r.basic) {
            case Cube::TransList::RotateBasic::U: printf("U"); break;
            case Cube::TransList::RotateBasic::D: printf("D"); break;
            case Cube::TransList::RotateBasic::L: printf("L"); break;
            case Cube::TransList::RotateBasic::R: printf("R"); break;
            case Cube::TransList::RotateBasic::F: printf("F"); break;
            case Cube::TransList::RotateBasic::B: printf("B"); break;
        }
        if(r.rev) putchar('\'');
        if(i + 1 != (int)tr.size()) putchar(' ');
    }
}
posted @ 2023-04-21 09:23  JerryTcl  阅读(618)  评论(0)    收藏  举报