GAP简单介绍

本文最后更新于:2026年6月30日 晚上

最近在整理一些群论计算相关的东西,所以顺便把 GAP 的一些基本用法记录下来。GAP 不是用来替代证明的软件,但是它很适合用来做实验,比如构造例子、筛选候选群、计算共轭类和正规子群、调用小阶群库,或者把一个数学定义写成可以反复运行的程序。

这篇文章不准备系统介绍 GAP 的所有功能,只是记录我自己比较常用的一些入口。如果已经学过一点群论,但还没有怎么用过 GAP,那么下面这些例子基本可以直接复制到本地运行。

GAP 的官方网站是:https://www.gap-system.org/。安装页面是:https://www.gap-system.org/install/。官方 Reference Manual 可以在线查看:https://docs.gap-system.org/doc/ref/chap0_mj.html,也可以下载 PDF 版本:https://docs.gap-system.org/doc/ref/manual.pdf

1. GAP 是什么

GAP 是 Groups, Algorithms, Programming 的缩写。顾名思义,它最初就是围绕群、算法和编程设计的。现在 GAP 的功能已经扩展到更广的离散代数对象,例如半群、代数、有限域、矩阵群、特征标表等;不过从我自己的使用经验看,计算群论仍然是它最核心、最成熟的应用场景。

我对 GAP 的理解大致是:

\[ \text{数学定义} \longrightarrow \text{GAP 对象} \longrightarrow \text{可复现实验}. \]

它不是图形化软件,而是一个交互式解释型语言和算法库。我们可以在终端里直接输入命令,也可以把较长的计算写成 .g 脚本,然后用 Read 运行。对于研究中的辅助计算来说,我更倾向于后者,因为脚本可以保存、修改、复核,也方便在不同机器之间同步。

写这篇文章时,GAP 官网显示的最新版本是 4.16.0,发布日期为 2026-06-02。本机 GAP 输出的版本为:

1
Print(GAPInfo.Version, "\n");

输出:

1
4.16dev

2. GAP 的安装

GAP 的安装方式和操作系统有关。最直接的方式是从官方安装页面下载对应系统的版本,然后按照页面提示安装。官方安装页面会分别给出 Linux、macOS 和 Windows 的说明。

如果使用 Linux,一般可以先尝试系统自带的软件包管理器。例如在 Ubuntu 或 Debian 中,可以先查看仓库里是否有 GAP:

1
apt search gap

如果仓库里的版本满足需求,可以直接安装:

1
sudo apt install gap

这种方式比较省事,但版本可能不是最新。如果需要最新版,或者需要完整编译某些 package,可以从官网下载安装包,然后按照官方说明安装。Ubuntu 或 Debian 下从源代码编译时,通常需要先安装一些编译工具和依赖:

1
sudo apt-get install build-essential autoconf libtool libgmp-dev libreadline-dev zlib1g-dev

解压官方压缩包之后,在 GAP 目录下运行:

1
2
./configure
make

如果还要编译 package,可以进入 pkg 目录并运行:

1
../bin/BuildPackages.sh

macOS 用户可以参考官方安装页面,也可以使用 Homebrew 相关方式安装。Windows 用户如果熟悉 Linux 环境,官方更推荐通过 WSL 使用 GAP;如果只是普通使用,也可以下载 Windows 的 .exe 安装包。

安装完成之后,在终端输入:

1
gap

如果能进入 gap> 提示符,就说明 GAP 已经可以正常启动。也可以在 GAP 中输入:

1
Print(GAPInfo.Version, "\n");

查看当前安装的版本。

3. 一次 GAP 会话

启动 GAP 通常只需要在终端输入:

1
gap

进入之后会看到 gap> 提示符。GAP 命令通常以分号 ; 结束:

1
2^20;

输出:

1
1048576

如果使用两个分号 ;;,GAP 会执行命令,但不打印返回值。这在给中间变量赋值时很常用:

1
2
S5 := SymmetricGroup(5);;
Order(S5);

输出:

1
120

退出 GAP 使用:

1
QUIT;

如果代码稍微长一点,我一般会写到文件里。例如把代码保存为 example.g,然后在 GAP 里运行:

1
Read("example.g");

这样做的好处很明显:交互式命令适合临时试探,脚本适合正式记录。

4. 查帮助和处理错误

GAP 的 manual 很厚,不太适合从头读到尾。更实际的方式是先掌握基本语法,遇到具体函数时再去查帮助。

1
2
3
?ConjugacyClasses
??centralizer
?SmallGroup

其中 ?name 用来查比较精确的条目,??keyword 更像关键词搜索。加载 package 之后,相关 package 的文档也可以进入帮助系统。

刚开始使用 GAP 时,还经常会遇到 brk>。例如输入:

1
Order(NotDefinedVariable);

可能得到:

1
2
3
Error, Variable: 'NotDefinedVariable' must have a value
not in any function at *stdin*:1
brk>

这里的 brk> 不是 GAP 崩溃了,而是 GAP 进入了 break loop,也就是停在错误现场供用户调试。对普通使用来说,最常见的处理方式是输入:

1
quit;

这会从 brk> 回到正常的 gap>。注意,小写 quit; 是退出当前 break loop;大写 QUIT; 是退出整个 GAP 会话。

5. 几个基本语法习惯

GAP 的语法不复杂,但有几个习惯需要先适应。

第一,赋值用 :=,相等判断用 =

1
2
x := 2^10;
x = 1024;

输出:

1
2
1024
true

第二,GAP 的列表从 1 开始编号:

1
2
3
L := [10, 20, 30];;
L[1];
L[3];

输出:

1
2
10
30

第三,ListFilteredSet 是非常常用的列表操作:

1
2
3
List([1..10], n -> n^2);
Set([3,1,3,2]);
Filtered([1..30], IsPrimeInt);

输出:

1
2
3
[ 1, 4, 9, 16, 25, 36, 49, 64, 81, 100 ]
[ 1, 2, 3 ]
[ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ]

含义分别是:

  • List(list, f):把函数 f 作用到列表的每个元素;
  • Filtered(list, condition):保留满足条件的元素;
  • Set(list):排序并去重。

函数的写法也比较直接。比如找出大于 n 的第一个素数:

1
2
3
4
5
6
7
8
9
10
FirstPrimeAfter := function(n)
local m;
m := n + 1;
while not IsPrimeInt(m) do
m := m + 1;
od;
return m;
end;;

FirstPrimeAfter(100);

输出:

1
101

在稍长一点的计算里,我通常会把数学条件先写成函数,然后再对一批群做筛选。这样比把所有判断都写在一个循环里清楚得多。

6. 置换和群

GAP 对置换的支持非常自然,可以直接使用循环记号:

1
2
3
4
5
6
a := (1,2,3);;
b := (1,2);;
a*b;
b*a;
a^2;
Order(a*b);

输出:

1
2
3
4
(2,3)
(1,3)
(1,3,2)
2

这里 a*bb*a 的输出不同,直接反映了置换群的非交换性。实际写脚本的时候,最好始终保持同一种乘法顺序习惯,不要凭直觉来回切换。

常见群可以直接构造:

1
2
3
4
5
6
S5 := SymmetricGroup(5);;
A5 := AlternatingGroup(5);;

Order(S5);
Order(A5);
StructureDescription(S5);

输出:

1
2
3
120
60
S5

这里比较常用的几个函数是:

  • Order(G):计算群阶;
  • StructureDescription(G):给出一个便于阅读的结构描述;
  • GeneratorsOfGroup(G):返回生成元。

需要注意的是,StructureDescription 很适合阅读,但不应该总被当成严格的规范形式。真正需要复现实验时,最好同时记录 IdGroup(G) 或其他更明确的不变量。

7. 共轭类、中心化子和正规子群

S5 为例,可以这样计算共轭类:

1
2
3
S5 := SymmetricGroup(5);;
cc := ConjugacyClasses(S5);;
List(cc, c -> [Size(c), Order(Representative(c))]);

输出:

1
2
[ [ 1, 1 ], [ 10, 2 ], [ 15, 2 ], [ 20, 3 ],
[ 20, 6 ], [ 30, 4 ], [ 24, 5 ] ]

这里列出的是每个共轭类的大小和代表元阶数。很多定义检查都可以先降到共轭类代表元上,而不必从全部元素开始。

再看中心化子和正规子群:

1
2
3
4
5
6
7
x := (1,2,3)(4,5);;
C := Centralizer(S5, x);;
Order(C);
StructureDescription(C);

List(NormalSubgroups(S5),
N -> [Order(N), StructureDescription(N)]);

输出:

1
2
3
6
C6
[ [ 120, "S5" ], [ 60, "A5" ], [ 1, "1" ] ]

这类输出通常可以作为进一步计算前的结构信息。比如在做较大搜索之前,先看正规子群、商群或者中心化子,往往能很快判断某条路线是否值得继续。

8. Small Groups 数据库

Small Groups 数据库是 GAP 中非常常用的工具。它可以用统一编号调用小阶群,也可以反过来识别某个群在数据库中的编号。对有限群计算来说,这个功能非常方便。

1
2
3
4
5
G := SmallGroup(60, 5);;
IdGroup(G);
StructureDescription(G);
IsSimple(G);
NrSmallGroups(120);

输出:

1
2
3
4
[ 60, 5 ]
A5
true
47

这里 SmallGroup(n,i) 表示小阶群库中第 in 阶群;IdGroup(G) 返回可复现的 [order,id]NrSmallGroups(n) 返回 n 阶群的个数。

例如,12 阶群一共有 5 个:

1
2
List([1..NrSmallGroups(12)],
i -> StructureDescription(SmallGroup(12, i)));

输出:

1
[ "C3 : C4", "C12", "A4", "D12", "C6 x C2" ]

小阶群库很方便,但也要注意计算规模。演示的时候可以选 12、24、60、120 这类小阶数;如果要遍历大量群,尤其是接近数据库上界的阶数,最好写成脚本离线运行,并把中间结果保存下来。

9. 商群、直积和特征标表

直积可以直接使用 DirectProduct

1
2
3
G := DirectProduct(CyclicGroup(2), AlternatingGroup(5));;
Order(G);
StructureDescription(G);

输出:

1
2
120
C2 x A5

商群通常写成 G / N,其中 NG 的正规子群:

1
2
3
4
5
S4 := SymmetricGroup(4);;
N := AlternatingGroup(4);;
Q := S4 / N;;
Order(Q);
StructureDescription(Q);

输出:

1
2
2
C2

GAP 也可以处理特征标表。例如 S5 的不可约普通特征标:

1
2
tbl := CharacterTable(SymmetricGroup(5));;
Display(Irr(tbl));

输出:

1
2
3
4
5
6
7
[ [   1,  -1,   1,   1,  -1,  -1,   1 ],
[ 4, -2, 0, 1, 1, 0, -1 ],
[ 5, -1, 1, -1, -1, 1, 0 ],
[ 6, 0, -2, 0, 0, 0, 1 ],
[ 5, 1, 1, -1, 1, -1, 0 ],
[ 4, 2, 0, 1, -1, 0, -1 ],
[ 1, 1, 1, 1, 1, 1, 1 ] ]

如果安装了 character table library,还可以访问大量已知有限群的特征标表。实际使用中,特征标表经常会和共轭类、正规子群、商群、群扩张等计算配合起来。

10. 一个例子:rational group

下面用 rational group 做一个稍微接近研究的例子。

有限群 (G) 称为 rational group,如果对任意 (xG),以及任意满足

\[ (k, |x|)=1 \]

的整数 (k),元素 (x) 与 (x^k) 在 (G) 中共轭。

按照定义直接检查所有元素当然可以,但会有很多重复。因为共轭性本身就是定义的一部分,所以我们可以先取共轭类代表元,然后只对代表元做检查:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
IsRationalGroupLocal := function(G)
local classes, rep, order, k;
classes := ConjugacyClasses(G);
for rep in List(classes, Representative) do
order := Order(rep);
for k in [1..order] do
if Gcd(k, order) = 1 then
if not IsConjugate(G, rep, rep^k) then
return false;
fi;
fi;
od;
od;
return true;
end;

然后对几个例子运行:

1
2
3
4
5
6
7
8
9
10
11
groups := [
["S5", SymmetricGroup(5)],
["A5", AlternatingGroup(5)],
["C2 x S5",
DirectProduct(CyclicGroup(2), SymmetricGroup(5))]
];;

for pair in groups do
Print(pair[1], ": order=", Order(pair[2]),
", rational=", IsRationalGroupLocal(pair[2]), "\n");
od;

输出:

1
2
3
S5: order=120, rational=true
A5: order=60, rational=false
C2 x S5: order=240, rational=true

这个例子的重点不是函数本身,而是一个常见思路:

  1. 先把数学定义拆成明确的可计算条件;
  2. 再利用群论结构减少重复检查,例如共轭类代表元;
  3. 对一批候选群批量运行;
  4. 同时输出阶、结构描述或者数据库编号,方便后续复核。

很多群论计算都可以按这个模式组织。先用小例子验证思路,之后再扩展到较大的搜索。

11. Package 和记录习惯

GAP 的很多功能由 package 提供。加载 package 可以使用:

1
2
3
LoadPackage("smallgrp");
LoadPackage("ctbllib");
LoadPackage("sonata");

成功加载通常返回 true,未安装或者不可用时可能返回 fail。如果要分享代码,最好在脚本开头写清楚依赖哪些 package。否则同一段代码在不同机器上可能表现不同。

我自己写 GAP 脚本时,一般会注意下面几件事:

  • 尽量同时记录 Order(G)IdGroup(G)StructureDescription(G)
  • 对共轭类、子群列表等结果做排序,减少无关顺序差异;
  • 大搜索前先用便宜条件筛选,例如阶、可解性、正规子群、商群结构;
  • 对较大的群,谨慎使用 Elements(G) 这类可能很重的操作;
  • 耗时搜索分段保存结果,不要把所有结果都压在一次交互式会话里;
  • 代码开头写清楚 package 依赖,输出里保留足够多的不变量。

还有几个细节也很容易踩坑:

  • 赋值用 :=,比较用 =
  • ; 会打印结果,;; 会抑制输出;
  • 看到 brk> 时,用 quit; 回到 gap>
  • StructureDescription 方便阅读,但复现时最好再记录 IdGroup
  • 大规模搜索最好脚本化,不要全靠终端临时输入。

12. 总结

我觉得 GAP 的长处,是把群论里的许多对象和操作直接变成了可运行、可复核的计算实验。对学习者来说,它可以用来验证例子和检查直觉;对研究中的辅助计算来说,它可以帮助我们构造群、筛选候选对象,检查共轭类、正规子群、商群和特征标表。

如果刚开始学习 GAP,可以按下面的顺序练习:

  1. 熟悉基本语法:赋值、列表、函数、循环;
  2. 熟悉置换和置换群:GroupSymmetricGroupOrder
  3. 熟悉常用群论函数:ConjugacyClassesCentralizerNormalSubgroups
  4. 学会使用小阶群库:SmallGroupIdGroupNrSmallGroups
  5. 把一个数学定义写成函数,再对一批例子运行;
  6. 遇到问题马上查帮助:?函数名??关键词

掌握这些之后,GAP 就不只是一个计算器,而会变成一个很实用的群论实验环境。


GAP简单介绍
https://www.zhouw.top/2026/06/30/GAP 简单介绍/
作者
Dabod Zhou
发布于
2026年6月30日
更新于
2026年6月30日
许可协议