Todd-Coxeter 算法和 3D/4D 均匀多胞体
本文介绍我写的一个高颜值的、脱离了低级趣味的小程序:用 Python 和 POV-Ray 绘制各种三维多面体和四维多胞体,代码在 Github 上。
以下是用这个程序渲染的一些例子,其中不同颜色的顶点/边/面表示它们在对称群的作用下位于不同的轨道中,具体解释见后。
例子
所有的 Platonic 多面体,Archimedean 多面体,比如 snub dodecahedron:
所有的 Kepler-Poinsot 多面体,比如 great icosahedron:
所有的四维均匀多胞体 (除去一个特例 The grand antiprism),比如我的 Github 头像 (runcinated 120-cell):
截断的四维正方体 truncated tesseract:
4d cube:
也可以是非凸的,比如星状正多胞体中的 grand stellated 120-cell:
甚至是 5 维欧氏空间中的均匀多胞体,如 5-cube:
等等,可玩的效果是非常多的。
以上这些都是在 Python 中做好计算以后,将多胞体的数据导出到 POV-Ray 中渲染得到的。你完全可以通过改写代码中的 POV-Ray 的部分来渲染得出不同的效果,当然前提是需要掌握 POV-Ray 的场景描述语言,不过这属于另一段故事,就不在本文的讨论范围内了。
下面介绍程序背后的数学原理。
这些图画的都是什么?
这些图都是三维或者四维欧氏空间中凸/非凸的均匀多胞体 (polytope),三维的情形更常用的称呼是多面体。这里有几个关键词需要注意:凸/非凸、均匀。
凸比较好理解,就是指多胞体上任意两点间的连线仍然属于此多胞体,否则就是非凸。上面的例子中 Platonic 多面体、Archimedean 多面体都是凸的;但 Kepler-Poinsot 多面体、星状正多胞体都是非凸的。
均匀这个词就不太好理解了。简单说就是:多胞体的所有顶点都一样,且每个二维的面都是正多边形,每个三维的胞腔都是均匀多面体(这是个递归的定义)。
要准确解释什么叫所有顶点都一样,就要用到群论的语言:一个多胞体
这些图是怎么画出来的?
这些多胞体看起来样子大不相同,但它们都可以用同一种方法计算出来,叫做
Wythoff
构造法,也称万花筒构造法。它的原理跟我们小时候玩的万花筒的原理是一样的:在空间中放置若干过原点的反射平面
(镜子),镜面之间的夹角是精心设计好的,都形如
这里的关键问题有两个:
- 对于不同的均匀多胞体,应该如何放置这些镜面,并选择初始顶点?
- 摆好镜面和初始顶点以后,怎样不重复不遗漏地计算初始顶点的所有镜像?
第一个问题的答案叫做 Coxeter-Dynkin 图,Coxeter-Dynkin 图是一个带标记信息的无向图,它编码了多胞体的全部信息,即只要知道了多胞体对应的 Coxeter-Dynkin 图,就可以求出该多胞体的所有数据 (仅缩放大小和在空间中的摆放位置除外)。每个均匀多胞体都有一个 Coxeter-Dynkin 图与之对应,但是不同的 Coxeter-Dynkin 图可能描述的是相同的多胞体。
比如正方体的 Coxeter-Dynkin 图为:
我们来解释这个图的含义:
在一个 Coxeter-Dynkin
图中,每个顶点代表一面镜子,在上图中有三个顶点,所以有三面镜子。将这三面镜子从左到右依次记作
- 若两个镜面之间的夹角为
则它们之间没有边相连。 - 若两个镜面之间的夹角为
则它们之间用一条无标记的边相连。 - 若两个镜面之间的夹角为
( 为有理数且 ),则它们之间用一条标号为 的边相连。
此外用圈出的镜面来标记初始顶点的位置,一个镜面被圈出当且仅当初始顶点不在这个镜面上。
从而在正方形的情形
于是这三面镜子可以这样摆放:
- 镜子
的法向量可以随意,比如 。 - 镜子
的法向量 与 夹角为 ,于是 可以取为 。 - 镜子
的法向量 与 垂直,所以 形如 ,它与 夹角是 ,所以 ,再结合 是单位向量, ,解出 即可。
要选择一个落在
求解这个线性方程组即可。
我们前面提到过,要使得初始顶点的所有镜像恰好构成一个均匀多胞体,镜子之间的夹角必须精心设置,这实际上只有有限种可能。换句话说,只有有限个 Coxeter-Dynkin 图可以给出 3D/4D 的均匀多胞体。在 维基百科 上完整的列出了每种均匀多胞体对应的 Coxeter-Dynkin 图,这里就不再专门列举了,但是特别指出两点:
- Coxeter-Dynkin 图的标号完全决定了多胞体的对称性,而初始顶点的位置则决定了多胞体的截断类型。
- 对偶的多胞体具有相同的 Coxeter-Dynkin 图,只不过要把边的标号从右到左反过来。比如正八面体和正方体的 Coxeter-Dynkin 图是一样的,但是边的标号是 (3, 4)。
第二个问题的答案叫做 Todd-Coxeter 算法,展开讲的话比较复杂,我们单列一节专门谈谈。
有限表现群和 Todd-Coxeter 算法
怎样求出初始顶点在所有镜子中的镜像?有个简单的办法:只要反复地将初始顶点关于每个镜面作反射,算出得到的镜像点的坐标,并与之前得到的点的坐标相比较(浮点数比较需要在一定的误差范围内考虑),直到不再有新的镜像点出现为止,不就得到全部顶点集吗?这个方法确实可行,但是既笨又丑陋:它完全没有用到多胞体具有对称性这一事实!
这个程序采用的是基于符号计算的途径,这个方法可以精准地得出所有顶点/边/面的信息,代价就是涉及的数学略复杂。我们先回忆一下群在集合上的作用的轨道—稳定化子定理:
轨道 —
稳定化子定理. 设群
注: 和一般的约定不同,这里群在集合上的作用为作用在右边,主要是为了编程方便,实际上左边右边都一样。
这个定理告诉我们,如果群
于是给定一个均匀多胞体
- 根据
的 Coxeter-Dynkin 图确定其对称群 和初始顶点 。 - 定出
的稳定化子群 并求出 的一组陪集代表元。 - 将
中的每个陪集代表元作用在 上即得 的全部顶点。
我们仍然以正方体为例来讲解:正方体的 Coxeter-Dynkin 图是
仍然记三个镜面为
正方体的对称群
利用 Todd-Coxeter 算法 (后面有解释) 不难求出这个群包含 48
个元素,罗列如下:
计算边/面/胞腔的原理是类似的,但考虑的细节要多一些。比如我们想求出所有关于第
- 检查初始顶点
是否落在 上。如果答案为是,那么关于此镜面的反射保持 不变,此多面体不含类型 的边。否则设 关于 的镜像为 ,则 构成一条类型为 的边 。 - 关于
的反射 把 和 互换,从而保持 不变。注意其它任何与 垂直并且包含初始点 的镜面反射也会保持 不变。在正方形的情形中,反射 互换 的两端因而保持 不变,此外镜面 和 是垂直的,且 包含在 中,所以反射 保持 上的每个点不变,于是 的稳定化子群为 。显然 同构于 ,所以 ,从而 ,即正方体有 12 条边 3。 - 求出
的一组陪集代表元并作用在 上得出全部类型为 的边。
求面的情形复杂一些,基本原理是这样的:
- 对
,如果初始顶点 不同时属于镜面 和镜面 ,则旋转 就可以生成一个面 。需要注意的是,如果这两个镜面恰好垂直,则必须二者均不包含 才能得到一个非退化的面,这个面是个正方形。在正方体的情形, 可以生成一个面, (两镜面垂直但只有一个镜面包含 )和 (两镜面均包含 )都不能给出面。 的稳定化子群是由 和那些包含 且与 均垂直的镜面反射生成。在正方形的情形是 4,显然 同构于二面体群 ,因此 , ,即正方体有 6 个面。
总之关键的步骤都是给定群
这里用到的算法叫做 Todd-Coxeter 算法。
Todd-Coxeter 算法在许多抽象代数或者群论的教材都有,用到的数学知识并不复杂。但完整描述并证明一份程序实现的细节还是很费功夫的,恐怕要好几页纸才能说清楚。限于篇幅,我这里仅用正方体的情形为例说明算法的步骤,具体的证明和更多的细节读者请参考
Handbook of Computational Group Theory, Holt, D., Eick, B., O’Brien, E.
中的 coset enumeration 一章。我个人认为这是讲解 Todd-Coxeter 算法最棒的文献。
Todd-Coxeter 算法非常类似玩数独游戏,这里要填的表是一个变化的二维数组
例:
设
我们先罗列一下这个数独游戏已知的关系:
已知关系:
- 对
的任何生成字 有 ,即 。注意此关系仅要求对 成立。 - 对任何陪集
和 的任何生成关系 有 ,即 以及 。注意此关系要求对所有陪集成立。
这些关系可以存储在两个列表里面,每个关系用一个数组表示。
第一个列表存储的是
的生成字列表:
- (1,)
- (2,)
第二个列表存储的是
的生成关系列表:
- (0, 0)
- (1, 1)
- (2, 2)
- (0, 1, 0, 1, 0, 1, 0, 1)
- (1, 2, 1, 2, 1, 2)
- (0, 2, 0, 2)
其中每条关系前面的数字是我们加上的编号以便于称呼。
注:
在非 Coxeter 群的情形还要把每个生成元的逆也作为生成元加入,其在
初始时刻表格
其中
我们来开始扫描
(1). 对第 0 条关系
0 | 0 |
第一个列表扫描完毕,接下来扫描第二个列表。
(2). 对第 2 条关系
1 | 0 | 0 | |
0 |
注:
每次定义新陪集时,比如定义
(3). 第 3 条和第 4 条关系已经满足,继续。
(4). 第 5 条关系,
1 | 0 | 0 | |
0 | 2 | ||
1 |
但是
1 | 0 | 0 | |
0 | 2 | ||
3 | 1 | ||
2 |
于是现在关系变成了
但是
1 | 0 | 0 | |
0 | 2 | ||
3 | 1 | ||
2 | 3 |
注: 在实际的程序实现中,我们总是从关系的两头同时开始扫描,直到它们相遇为止。
(5). 关系 6 已经满足,继续。
(6). 对关系 7
1 | 0 | 0 | |
0 | 2 | 1 | |
3 | 1 | ||
2 | 3 |
至此对
注意:从现在起至程序结束,我们不再使用第一个列表。
下面开始扫描
(1). 经检查关系 2, 3, 4, 5 已经满足,继续。
(2). 对关系 6 有
1 | 0 | 0 | |
0 | 2 | 1 | |
3 | 1 | 4 | |
2 | 3 | ||
4 | 2 |
(3). 关系 7 已经满足,从而
下面开始扫描
(1). 经检查关系 2, 3, 4, 5, 6 都已经满足,继续。
(2). 对关系 7 有
1 | 0 | 0 | |
0 | 2 | 1 | |
3 | 1 | 4 | |
2 | 3 | 5 | |
5 | 4 | 2 | |
4 | 3 |
我把
1 | 0 | 0 | |
0 | 2 | 1 | |
3 | 1 | 4 | |
2 | 3 | 5 | |
5 | 4 | 2 | |
4 | 6 | 3 | |
5 | 6 |
1 | 0 | 0 | |
0 | 2 | 1 | |
3 | 1 | 4 | |
2 | 3 | 5 | |
5 | 4 | 2 | |
4 | 6 | 3 | |
7 | 5 | 6 | |
6 | 7 |
扫描
1 | 0 | 0 | |
0 | 2 | 1 | |
3 | 1 | 4 | |
2 | 3 | 5 | |
5 | 4 | 2 | |
4 | 6 | 3 | |
7 | 5 | 6 | |
6 | 7 | 7 |
扫描
至此
由此利用宽度优先搜索不难得出陪集间的关系为:
从而其一组陪集代表元可以选为
这正是我们前面看到过的。
注: 这个例子看似有点长,但还是一个比较简单的例子,其中并没有出现已知陪集重复的情形(Holt 的书中称之为 coincidence)。这种情形的处理麻烦一些,因为一旦出现重复的陪集,就有可能顺藤摸瓜找到更多重复的陪集。这时就要立刻暂停扫描,流程跳转到处理 coincidence:开辟一个栈来存放已知的 coincidence,每次弹出一对,将它们合并,然后把新发现的 coincidence 压入栈中。
星状多胞体的计算
星状多胞体也可以使用 Wythoff 构造法来生成,但是直接套用上面的方法一般是行不通的,我们需要在生成元中加入额外的生成关系。
这里以 Great dodecahedron 为例来说明:其 Coxeter-Dynkin 图为
于是三面镜子的法向量夹角分别为
这是一个无限群,而且顶点的稳定化子的商群也是无限的,所以还想按以前的方法计算就行不通了。
实际上我们只要在生成关系中再加上一条
注意到我使用了字母
(请忽略左边错误的 Coxeter 图,这个 ui 界面我改不动)
由视频可见,Great dodecahedron 与正二十面体 (icosahedron)
共用相同的顶点,并且看起来 Great dodecahedron 可以通过在 icosahedron
表面挖一些三角形的洞得到。这个结论也可以推广:一般地如果星状多面体的洞是一个有

在上图中,
Great dodecahedron 可以这样得到:沿着正二十面体的边从顶点
像这样对一个多面体,保持它的顶点和边的集合不变,但是每次选择右手边的第
我们来导出正二十面体的对称群
来看三角形
Facetting 操作
首先
其次
它俩的区别在于边组成面的方式不一样。
我们来找出
注意到
所以我们就可以对
这个额外的生成关系其实也有背后的解释:对 Faceting
手术得到的新多面体再进行一次 Faceting 手术是可以回到原来的多面体的。对
great dodecahedron
每次沿着它的边,选择当前离开的边的右手第二个边走下去,即从
这一点从群上也可以得到验证。
关于 Faceting 手术可以在 McMullen 和 Schulte 所著的 Abstract Regular Polytopes 一书中找到。
Snub cube 的计算
如果你理解了上面的内容,snub 多面体的情形也是不难理解的。我以 snub cube 来说明:
Snub cube 和 cube 的区别在于它的对称群只包含旋转,我们已经看到 cube
的对称群
不难写出
利用 Todd-Coxeter 算法不难求出这个群的所有 24 个元素:
注意在 snub 的情形初始顶点
我们现在利用轨道—稳定化子的理论来求 snub cube 的边。snub cube
的边也是分类型的,每个
首先注意到任何
于是 snub cube 的类型为
snub cube 的面可以这样求:由于
于是
小心!我们还漏掉了一种三角面,它源自
于是 snub cube 一共有 14+24=38 个不同的面。
这里介绍的方法也适用于其它的 snub 多面体以及 snub 24-cell。
多面体的顶点投影到 Coxeter 平面
项目中还实现了一个
draw_on_coxeter_plane(*args, **kwargs)
方法,用于绘制将多面体的顶点投影到其 Coxeter
平面上后得到的图案,例如下图显示的是将 600-cell 的 120 个顶点投影到其
Coxeter 平面上的结果:
你可以和 wikipedia 上的效果 比较一下。
附录:手算 Todd-Coxeter
对简单的群,Todd-Coxeter 算法完全可以用手算快速得出结果。我非常推荐 Borcherds 的视频,他的演示非常精彩:
仿照 Borcherds 的方法,前面正方形的例子可以很快写出来:
我来解释一下步骤:我们将画出一个有限图,图的每个顶点代表
- 首先我们在空白纸上画出第一个顶点,它对应的陪集是
自身。 包含 ,所以红、绿边是自边。 ,即蓝色的边,会把它映射为一个新顶点 。 - 从
出发,利用红蓝交换,可得红色保持 不动。但是绿蓝不交换,所以绿色将 映射为新顶点 。 ,即 ,所以 所以蓝色保持 不动。红绿不交换,所以红色将 映射为新顶点 。- 仿照上面的分析继续下去,可以发现到
时,三种颜色的边不会给出新顶点。所以 就是 的全部陪集。