アルゴリズム(algorithm2e / algpseudocode)

「latex アルゴリズム パッケージ」で検索すると、ほとんど同じ名前が四つ返ってきます——algorithmalgorithmicalgorithmicxalgorithm2e。しかも、いま直そうとしているプリアンブルが、同じ文書に同居できない二つを読み込んでいる可能性はかなりあります。このもつれこそが本題です。ほどけてしまえば、LaTeX で擬似コードを組むのは難しくないからです。押さえておきたいのは、擬似コードが コードの貼り付けではない ということ。実在のソースを載せる姉妹ページと違い、アルゴリズムは数式に近い作法で組まれます——変数はイタリック、代入は矢印、キーワードは太字。しかもそれが、\ref で参照できる番号付きのフロートに入ります。以下では、結局どの組み合わせを読み込むのか、間違えたときに出る実際のエラー文、そして二つの流儀それぞれの完全な書き方を順に見ていきます。

擬似コードと listings の違い——貼り付けるのか、組版するのか

違いは一言でいうと、listings は文字を保存し、擬似コードは意味を組版するということです。listingsminted に渡した中身は逐語環境として扱われ、打ち込んだ空白や記号がそのまま紙に出ます。だからこそ実在のソースコードに向いています(その使い分けは姉妹ページに譲ります)。ところが擬似コードでは事情が逆になります。$y \gets 1$ と書けば、y はイタリックの変数として、\gets は矢印 ← として組まれる——つまり中身は数式モードの住人で、逐語ではありません。キーワードだけが太字のローマン体で立ち上がり、変数と視覚的に区別されます。この「変数はイタリック、キーワードは太字」という約束は、クヌースの『The Art of Computer Programming』以来の教科書の作法をそのまま紙面に写したものです。ちなみに \gets という名前自体が計算機科学由来で、「x gets 1」=代入の読みから来ています。

もう一つの違いは置き場所です。lstlisting は本文の流れの中に居座りますが、擬似コードは figuretable と同じ フロート(浮動体) に入れるのが定石です。つまり LaTeX がページの切れ目を見て適切な位置へ運び、上部に「Algorithm 1」という通し番号付きの見出しが付き、\listofalgorithms で一覧に載る。この「入れ物としてのフロート」と「中身としての擬似コード」が別々のパッケージから来る、という一点さえ飲み込めば、四つの似た名前は一気に整理できます。

結局どれを \usepackage するのか——四つの名前の正体

新しく書き始めるなら答えは二行です。\usepackage{algorithm}\usepackage{algpseudocode} を並べるか、\usepackage{algorithm2e} を単独で読むか。この二択以外の組み合わせはたいてい事故を起こします。なぜ紛らわしいかというと、CTAN のパッケージ名とスタイルファイル名がずれているからです。algorithm.styalgorithm という名前のパッケージからではなく、algorithms バンドル(複数形)から来ます。一方 algpseudocode.styalgorithmicx から来ます。つまり、二つのファイル名を書いているのに、実際にインストールしているのは名前の違う二つのバンドルなのです。

バンドル入っているスタイルファイル役割
algorithmsalgorithm.sty, algorithmic.sty入れ物(algorithm フロート)と、1994 年に遡る古い中身 algorithmic。中身のほうは大文字命令(\STATE)で、いまは非推奨
algorithmicxalgorithmicx.sty, algpseudocode.sty, algcompatible.sty中身だけ。Szász János による 2005 年の書き直しで、algpseudocode が標準レイアウト。入れ物は付いてこない
algorithm2ealgorithm2e.sty入れ物と中身を一つで賄う独立系。Christophe Fiorio が 1996 年から保守。algorithm とは併用しない
preamble
% Camp 1 -- two packages, two jobs. This is the usual choice.
\usepackage{algorithm}      % the float: \begin{algorithm}, \caption, \listofalgorithms
\usepackage{algpseudocode}  % the body:  \begin{algorithmic}, \State, \If, \For

% Camp 2 -- one package does everything. Do NOT also load algorithm.
\usepackage[ruled,vlined,linesnumbered]{algorithm2e}

この分業がどれほど徹底しているかは、algorithm.sty を開いてみるとわかります。中身は 100 行足らずで、擬似コードに関する記述は一行もありません。やっているのは float パッケージを読み込み、\newfloat{algorithm}{htbp}{loa} で新しいフロート型を一つ定義し、その名前を「Algorithm」と決め、\listofalgorithms を生やす——それだけです(TeX Live 2024 同梱版は 2009 年 8 月 24 日付の v0.1)。副作用として面白いのが、未知のオプションをすべて フロートの名前 として解釈する仕掛けが入っていること。\usepackage[Procedure]{algorithm} と書くと、以後のキャプションはすべて「Procedure 1」「Procedure 2」になります。

Command \algorithm already defined. が出たとき

このエラーの意味は一つだけです——algorithmalgorithm2e を両方読み込んでいます。どちらも algorithm という名前の環境を作ろうとするので、後から来たほうが先客にぶつかります。プリアンブルからどちらか一方を消せば直ります。同じ理由で、読み込む順番を入れ替えると文面も変わり、algorithm2e を先に、algorithm を後に書いた場合は ! LaTeX Error: Command \listofalgorithms already defined. になります。原因は同じです。もう一つよくあるのが ! LaTeX Error: Command \algorithmic already defined. で、これは古い algorithmic と新しい algpseudocode を同時に読んだときの症状。algorithmicx 系は algorithmic の後継なので、両方は要りません。

preamble
% Each of these three preambles stops the compile.
\usepackage{algorithm}\usepackage{algorithm2e}
%   ! LaTeX Error: Command \algorithm already defined.

\usepackage{algorithm2e}\usepackage{algorithm}
%   ! LaTeX Error: Command \listofalgorithms already defined.

\usepackage{algorithmic}\usepackage{algpseudocode}
%   ! LaTeX Error: Command \algorithmic already defined.

% If a class or a co-author really forces algorithm2e into a document that
% already owns the algorithm float, this option renames the environment to
% algorithm2e and \listofalgorithms to \listofalgorithmes, so nothing collides.
\usepackage[algo2e]{algorithm2e}

ところが、いちばん厄介な組み合わせはエラーを出しません。algorithm2ealgpseudocode を並べて読み込んでも、コンパイルは何事もなく通ります。algorithmicx\For\If といった利用者向けの名前を定義するとき、すでにその名前が存在するかを確かめ、存在していれば黙って定義を諦めるからです。結果として algorithm2e の意味が生き残り、algorithmic 環境の本文を書いたときになって初めて、! Missing number, treated as zero. のような無関係に見えるエラーが \EndWhile の行で吹き出します。原因からいちばん遠いところで壊れるわけで、明快なエラーより始末が悪いともいえます。なお algorithm2ealgo2e オプションを備えているのは、まさにこの手の衝突を避けるためで、その際 \listofalgorithms は綴りの違う \listofalgorithmes に変わります——このパッケージがフランス語圏で書かれてきた名残です。

\begin{algorithm} はフロート——\caption\label[H]

algorithm 環境は、figuretable と同じ意味で本物のフロートです。既定の配置指定は htbp(ここ・ページ上端・下端・独立ページの順に試す)で、目次ファイルの拡張子は .loa。したがって図表でおなじみの作法がそのまま通用します——\caption{…} が「Algorithm 1」という番号付きの見出しを作り、その 直後\label{alg:…} を置けば、本文から \ref{alg:power} で番号を、\pageref{alg:power} でページ番号を引けます。\label\caption より前に書くと、番号がひとつずれるか節番号を拾ってしまうので順序は守ってください。文書の頭に \listofalgorithms と書けば——\listoffigures\listoftables の兄弟です——「List of Algorithms」という一覧ができ、番号と \caption の文言が並びます。番号・参照・一覧が確定するには、いつもどおり 2 回コンパイル が必要です。どうしても浮かせたくない場合は \begin{algorithm}[H] と書けば、その場に固定されます(algorithmfloat パッケージを読み込むので [H] は追加設定なしで使えます)。

本文を書く——\State\If\For\While\Function

内側の環境の名前は algorithmic です——パッケージ名は algpseudocode なのに環境名は違う、という最後のひっかけ。命令はすべて 頭文字だけ大文字 で、\State が一行分の文を開き、ブロックは \If\EndIf\For\EndFor のように明示的に閉じます。開始時の任意引数 [1] は行番号の間隔で、[1] なら全行、[5] なら 5 行ごと、省略すれば番号なし。次の例は二分探索で、条件分岐・ループ・関数・事前条件・行末コメントをひととおり含みます。

document.tex
\documentclass{article}
\usepackage{algorithm}
\usepackage{algpseudocode}
\begin{document}
\listofalgorithms

\begin{algorithm}
  \caption{Binary search}\label{alg:bsearch}
  \begin{algorithmic}[1]
    \Require sorted array $a[1..n]$, key $k$
    \Ensure index of $k$, or $0$
    \Function{Search}{$a,k$}
      \State $lo \gets 1$;\ $hi \gets n$ \label{ln:init}
      \While{$lo \le hi$}
        \State $m \gets \lfloor (lo+hi)/2 \rfloor$ \Comment{midpoint}
        \If{$a[m] = k$}
          \State \Return $m$
        \ElsIf{$a[m] < k$}
          \State $lo \gets m+1$
        \Else
          \State $hi \gets m-1$
        \EndIf
      \EndWhile
      \State \Return $0$
    \EndFunction
  \end{algorithmic}
\end{algorithm}

Algorithm~\ref{alg:bsearch} halves the interval; the bounds are set on
line~\ref{ln:init}, page~\pageref{alg:bsearch}.

\end{document}

出力はこうなります。上下に罫線を引いた枠が浮動して置かれ、上端に「Algorithm 1 Binary search」。枠の中では \Require\EnsureRequire: / Ensure: という太字の見出しを作り、これらは番号の外側に置かれます。番号は \Function の行から始まって 1、2、3 … と左端に並び、\Comment{midpoint} は行末に小さな三角 ▷ を付けて ▷ midpoint と組まれます。\Function{Search}{$a,k$} の名前はスモールキャップで組まれ、本文中や別の行から呼び出したいときは \Call{Search}{$a,k$} と書きます。見出しの語を変えたい人が多いので付け加えると、Require: / Ensure: の文字列は \algorithmicrequire\algorithmicensure に入っています。\renewcommand{\algorithmicrequire}{\textbf{Input:}} と書けば Input: に変わります。

命令組まれ方メモ
\State番号付きの一行文ごとに一つ。忘れると前の行に続いてしまう
\Require / \EnsureRequire: / Ensure:事前条件・事後条件。行番号の外側に置かれる
\If ... \ElsIf ... \Else ... \EndIfifthen / else if / else / end if「else if」の綴りは \ElsIf(e が一つ足りない)。よく打ち間違える
\For ... \EndForfordoend for\ForAll{…}for all …。条件は波括弧で渡す
\While ... \EndWhilewhiledoend while\Repeat\Until{…} も同じ形で用意されている
\Function ... \EndFunctionfunction Name(引数) … end function名前はスモールキャップ。呼び出しは \Call{Name}{引数}。手続きは \Procedure
\Returnreturn\State \Return $y$ のように \State と組み合わせる
\Comment▷ コメント文行末に置く。三角は \algorithmiccomment で差し替えられる

この流儀でいちばん多い事故は、\State の書き忘れです。エラーは出ません。\State $x \gets 1$ の次に \State なしで $y \gets 2$ と書くと、出力は「1: x ← 1 y ← 2」となり、二つの文が同じ行に押し込まれて番号も一つ減ります。行数が合わないと感じたら、まずここを疑ってください。もう一つ、古い論文のテンプレートで \STATE\WHILE\ENDWHILE という大文字だけの命令を見かけたら、それは 1994 年由来の旧 algorithmic の書き方です。原稿をそのまま流用したいときは algpseudocode の代わりに \usepackage{algcompatible} を読み込むと、algorithmicx の土台の上で大文字命令がそのまま動きます。

行番号を振る、そして「3 行目」を参照する

行番号は \ref で引けます。algorithmic 環境を [1] で開いておき、参照したい行の 末尾\label{ln:init} を置けば、本文の \ref{ln:init} がその行の番号に展開されます。上の例では \Function の行が 1 なので、\ref{ln:init} は 2 になります。これは解説文を書くうえで想像以上に効きます。「3 行目の条件」と手で書いてしまうと、あとで一行足しただけで文章が嘘になるからです。algorithm2e 側でも作法は同じで、linesnumbered を有効にしたうえで行末の \; の後ろに \label{…} を置けば \ref が通ります。注意点は一つ、番号が振られていない行には \label を付けても意味がないこと——[1]linesnumbered も指定していなければ、参照先の番号自体が存在しません。

algorithm2e の書き方——波括弧と、忘れてはいけない \;

もう一方の流儀では、ブロックの終わりを \EndFor のような命令で示しません。中身を波括弧の引数として渡します——\For{条件}{中身}\While{条件}{中身}、if–then–else は \eIf{条件}{真}{偽}(else なしなら \uIf、一行に収めるなら \lIf)。入出力は \KwIn{…}\KwOut{…} で、太字の Input: / Output: として組まれます(Data: / Result: という語のほうがよければ \KwData{…}\KwResult{…})。そして最大の落とし穴が、各文を \; で閉じる ことです。忘れてもエラーにはならず、次の文が同じ行に続いてしまいます。出力に「;」の記号を出したくなければ \DontPrintSemicolon を書けば消えます(\; 自体は必要です)。見た目はすべて読み込みオプションで決まり、ruled(上下に罫線)・boxed(全体を箱で囲む)・vlinedlined(ブロックに縦線を引く二通り)・plain(既定)、そして行番号は linesnumbered。この三つの罫線オプションは、文書中で \SetAlgoVlined\SetAlgoLined\SetAlgoNoLine と書くのとまったく同じものです。TeX Live 2024 が同梱するのは 2017 年 7 月 18 日付の v5.2 です。

document.tex
\documentclass{article}
\usepackage[ruled,vlined,linesnumbered]{algorithm2e}
\begin{document}
\listofalgorithms

\begin{algorithm}
  \DontPrintSemicolon
  \KwIn{an array $a[1..n]$}
  \KwOut{the sum $s$ of its positive entries}
  $s \gets 0$\;\label{ln:zero}
  \For{$i \gets 1$ \KwTo $n$}{
    \eIf{$a[i] > 0$}{
      $s \gets s + a[i]$\;
    }{
      \tcp{negative or zero: skip}
    }
  }
  \Return $s$\;
  \caption{Sum of positive entries}\label{alg:sum}
\end{algorithm}

Algorithm~\ref{alg:sum} accumulates into $s$, initialised on line~\ref{ln:zero}.

\end{document}

出力の見出しは「Algorithm 1: Sum of positive entries」——番号のあとにコロンが入るのが algorithm フロートとの見た目の違いです。上に Input:Output: が並び、本体は for i ← 1 to n doifthenelse と組まれ、vlined を指定したのでブロックの左に縦線が引かれます。\tcp{…} は行内コメントで、// negative or zero: skip のように出ます。\caption を環境の 末尾 に書いている点に注目してください——algorithm2e ではこれが慣例で、ruled を指定していれば、末尾に書いても見出しは枠の上端に出ます。\listofalgorithms にもこの文言がそのまま並びます。ちなみに algorithm2ealgorithm 環境も既定ではフロートなので、その場に留めたいときは \begin{algorithm}[H] が使えます。

キーワードを英語以外にする——frenchgermanonelanguage

algorithm2e は擬似コードのキーワードを訳せます。この機能を持つのはこちらの流派だけで、algpseudocode にはありません。オプションとして frenchgermanngermanspanishitalianoportugueseczechslovakcroatianenglish が用意されています。ただし挙動には段差があります。\usepackage[french]{algorithm2e} とだけ書いた場合、フロートの名前は「Algorithme 1 :」に変わりますが、\For\eIf は英語のまま出ます。フランス語で組みたければ フランス語の命令名\Pour{…}{…}\Si{…}{…}\KwA)を使うのです。既存の原稿の命令名を書き換えたくないなら、onelanguage を足してください。\usepackage[french,onelanguage]{algorithm2e} と書けば、\For\eIf のまま pour i ← 1 à n fairesialorssinonfin と出力されます。ドイツ語なら [german,onelanguage]für i ← 1 bis n tuewenndannsonstEnde、見出しは「Algorithmus 1:」です。

preamble
% Keep the English command names, print French keywords.
\usepackage[french,onelanguage,ruled,linesnumbered]{algorithm2e}
%   \For{...}{...}  ->  pour i <- 1 a n faire ... fin

% Without onelanguage, only the float name is translated; use the
% French command names for a French body.
\usepackage[french,ruled]{algorithm2e}
%   \Pour{...}{...}, \Si{...}{...}, \KwA

結局どちらを選ぶか——algpseudocodealgorithm2e の比較

投稿先のテンプレートが指定しているなら、それに従うのが正解です——多くの学会クラスは片方を前提に余白や書体を調整しています。指定がなければ、擬似コードを数学の文章として書きたいなら algpseudocode入出力の宣言・縦線・キーワードの翻訳を使いたいなら algorithm2e。決め手は次の表です。

項目algorithm + algpseudocodealgorithm2e
packages二つ読み込む(入れ物+中身)一つで完結
block syntax\If{…}\EndIf と明示的に閉じる\eIf{…}{…}{…} と波括弧で渡す
input / output\Require / \Ensure(事前・事後条件)\KwIn / \KwOut(入力・出力の宣言)
end of line不要(\State が行を開く)\; が必須。忘れると次の文が同じ行に続く
line numbers\begin{algorithmic}[1] の任意引数で間隔を指定linesnumbered オプション(rightnl で右寄せも可)
keyword language英語のみ。\algorithmicwhile などを個別に再定義する必要があるfrenchgerman などのオプション。onelanguage で命令名は英語のまま訳せる

どちらを選んでも、掲載物としての扱いは同じです——\caption で番号付きの見出しを与え、\label\ref で本文から指し、\listofalgorithms で一覧を作り、番号を確定させるために二度コンパイルする。そして最後にもう一度だけ言うと、中身のパッケージは文書全体で一つに絞ってくださいalgorithm2e を使うなら algorithm は読み込まない。algpseudocode を使うなら algorithmic は読み込まない。この一行の規律が、冒頭で見た四つの紛らわしい名前をめぐる事故のほとんどを未然に防ぎます。なお、擬似コードではなく実在のソースコードを色付き・行番号付きで載せたい場合は、listingsminted を扱う姉妹ページのほうが目的地です。