把代码放进稿件的方法不止一种。verbatim 按输入原样输出;listings / minted 用颜色和行号展示真实源代码;第三种方式则把与语言无关的过程放进编号为 “Algorithm 1” 的框中,也就是 伪代码。伪代码不同于真实代码,它会像图或表一样作为 *浮动体* 排在页面上,并带有 标题、连续编号、交叉引用和算法目录。本页聚焦于如何把伪代码作为一个 *可列出的对象* 放得体面,尤其是标题和编号。对同一批包的逐命令完整说明在 /learn/math/algorithms;这里给出两个生态的地图和各自的最小完整示例。
伪代码作为“浮动体”排版
verbatim 和 listings 把代码放在 正文流中,而伪代码通常放进 浮动体。浮动体是像 figure(图)或 table(表)一样,由 LaTeX 自动放到合适位置、避开尴尬分页的盒子。这样有两个好处。第一,有一个带连续编号的 “Algorithm 1” 标题,正文中可以用 \ref 引用。第二,长算法不会在页边界处被难看地截断;内层正文环境若直接放在正文中可能跨页断开,但浮动体盒子保持完整,并作为整体移到下一页。
第一个容易困惑的点,是有好几个名字相近的包。关键在于一种分工:一个包提供容器(带编号的浮动框),另一个包提供内容(伪代码本身)。不过这种分工只适用于两大流派中的一派。实际使用中,大体有两种选择。
- 两层结构:
algorithm(容器)+algpseudocode(内容)。algorithm浮动体提供浮动框、标题和编号;algpseudocode(algorithmicx的标准布局)提供伪代码正文,如\State、\For、\If等。 - 一个包:
algorithm2e。 自成一体:它自带浮动体、标题和正文命令(\KwIn/\KwOut、\eIf等)。不要再加algorithm(两者都会定义同名浮动环境,发生冲突)。
最重要的原则先说在前面:整个文档要统一选用其中一个流派。algpseudocode 和 algorithm2e 在语法和理念上完全不同,同时加载会在 \For、\If 等同名命令上冲突并报错。无论选择哪一个,都像平常一样编译两次,让编号、交叉引用和算法目录稳定下来。
标题、编号、引用与算法目录
本页的重点是作为掲載物的体裁。在任一流派中,\caption{…} 都会生成 “Algorithm N” 这样的标题,N 按出现顺序自动编号。紧接着放 \label{key},正文就能用 \ref{key} 取出这个编号,用 \pageref{key} 取出页码;做法与引用图表完全相同。
带编号浮动体的另一个好处是 算法目录。它类似 \listoffigures 和 \listoftables;写下 \listofalgorithms 后,就会生成一个类似目录的列表,汇总各算法的编号和标题。列表中出现的是 \caption 给出的文字,在 algorithm 浮动体和 algorithm2e 下都一样有效。注意 algorithm2e 还提供与 \caption 不同、只加题名的 title 系命令,但它们不会向算法目录添加条目。
标题放置的 位置 随流派而不同。在 algorithm 浮动体中,和图一样,\caption 可以放在正文 前面或后面(很多人放在框的上方)。而在 algorithm2e 中,惯例是把 \caption 放在环境的 末尾,并且这个标题也会作为算法目录中的引用名。
流派 1:algorithm + algpseudocode
外层用 algorithm 浮动体包住,内层在 algorithmic 环境中写伪代码(包名是 algpseudocode,但环境名是 algorithmic)。正文命令都采用 首字母大写(如 \State、\While),这也是与全大写旧 algorithmic(\STATE)区分的方法。最常用的命令如下。
\State— 一条语句(一行)的开始;每条语句一个。\For{…}…\EndFor/\While{…}…\EndWhile— 循环。\If{…}…\ElsIf{…}…\Else…\EndIf— 条件分支。\Function{name}{args}…\EndFunction,以及\Return— 函数与返回值。\Require/\Ensure— 前置条件和后置条件(带粗体标签)。\Comment{…}— 行末注释。
开头的可选参数 [1] 控制行号:1 表示每行编号,n 表示每 n 行编号,省略则没有行号。下面是计算幂 y = xⁿ 的最小示例;注意 \label 放在 \caption 之后。
\documentclass{article}
\usepackage{algorithm}
\usepackage{algpseudocode}
\begin{document}
\listofalgorithms
\begin{algorithm}
\caption{Calculate $y = x^n$}\label{alg:power}
\begin{algorithmic}[1]
\Require $n \geq 0$
\State $y \gets 1$
\While{$n \neq 0$}
\State $y \gets y \times x$
\State $n \gets n - 1$ \Comment{count down}
\EndWhile
\State \Return $y$
\end{algorithmic}
\end{algorithm}
アルゴリズム~\ref{alg:power} は累乗を計算する。
\end{document}编译后,会得到一个上下带横线、在页面上浮动的框,顶部标题为 “Algorithm 1 Calculate y = xⁿ”(编号自动生成)。每行左侧会有 1, 2, 3 … 的行号,正文中的 \ref{alg:power} 会解析为该编号(这里是 1)。开头的 \listofalgorithms 会生成类似 “1 Calculate y = xⁿ … page” 的条目。关于每个命令的输出,包括 \ElsIf 和 \Function,请参见 /learn/math/algorithms。
流派 2:algorithm2e
algorithm2e 是另一个体系,把容器和内容 自成一体地放在一个包中(当前版本 5.2,2017 年)。在 \begin{document} 之前用 \usepackage[…]{algorithm2e} 加载;它的 algorithm 环境本身就是浮动体。外观通过加载选项选择:ruled(上下横线)、boxed(整体加框)、vlined/lined(块结构竖线)、plain(默认,无装饰);加 linesnumbered 则显示行号。语法要点有三条:
- 输入/输出使用专用命令
\KwIn{…}/\KwOut{…}(或\KwData{…}/\KwResult{…}),输出为粗体 “Input:”、“Output:” 等。 - 分支和循环把正文作为花括号参数传入。 if–then–else 写作
\eIf{cond}{then}{else};循环写作\For{…}{body}、\While{…}{body}(用\KwTo表示 “to”)。 - 每条语句以
\\;结尾(反斜杠加分号)。忘记它,下一条语句会接在同一行。若不想在输出中显示 “;”,使用\DontPrintSemicolon。
\documentclass{article}
\usepackage[ruled,linesnumbered]{algorithm2e}
\begin{document}
\listofalgorithms
\begin{algorithm}
\caption{Sum of positive entries}\label{alg:sum}
\KwIn{an array $a[1..n]$}
\KwOut{the sum $s$ of its positive entries}
$s \gets 0$\;
\For{$i \gets 1$ \KwTo $n$}{
\eIf{$a[i] > 0$}{
$s \gets s + a[i]$\;
}{
skip\;
}
}
\Return $s$\;
\end{algorithm}
アルゴリズム~\ref{alg:sum} は正の要素を合計する。
\end{document}ruled 选项在上下画横线并在顶部放标题行,linesnumbered 给每行编号。框开头有粗体 “Input:” 和 “Output:”;\For 行排成 for i ← 1 to n do,正文缩进;\eIf 把 if … then 的真分支放在 else 的假分支上方。每个 \; 处发生换行,最后一行是 return s。\caption 文字会原样出现在 \listofalgorithms 中。更细的控制,如 [H](固定在当前位置不浮动)、\SetAlgoLined(给块画竖线)等,详见 /learn/math/algorithms。
该用哪一个
两者都被广泛使用,差别更多是风格而不是优劣。可以用下面的速查表先判断方向。
| 角度 | algorithm + algpseudocode | algorithm2e |
|---|---|---|
構成 | 两层,两个包 | 一个自成一体的包 |
本体の書き方 | 以 \State 开头的直接命令体系 | 块正文作为花括号参数传入 |
入出力 | \Require / \Ensure | \KwIn / \KwOut(显式) |
行末 | 不需要 | 必须有 \\; |
向く人 | 想要传统外观;模板默认采用它 | 重用显式输入输出、竖线、多语言关键字 |
无论哪一派,作为掲載物的处理方式相同:用 \caption 给出带编号标题,用 \label/\ref 引用,用 \listofalgorithms 生成列表。再强调一次:正文包只选一个;如果使用 algorithm2e,不要加载容器包 algorithm。完整命令列表和更长的示例请见 /learn/math/algorithms。如果想展示带颜色和行号的真实源代码(不是伪代码),则应使用 /learn/code-verbatim/code-listings 中的 listings/minted。