의사코드를 조판하는 LaTeX 패키지에는 algorithm, algorithmic, algorithmicx(실제로 쓰는 것은 algpseudocode), algorithm2e 처럼 헷갈리는 이름이 늘어서 있고, 어느 것과 어느 것이 다투는지 알아내는 데서 처음 막힙니다. 그런데 정말 위험한 조합은 오류를 내는 쪽이 아닙니다. TeX Live 2024에서 모든 조합을 시험해 보니, algorithm2e와 algpseudocode를 함께 불러와도 읽어들일 때 오류가 전혀 나지 않고, \For를 쓰는 순간 출력만 조용히 망가졌습니다. 이 페이지에서는 두 층 구조(바깥틀과 내용)라는 설계를 정리한 뒤, 실제로 재현한 오류 메시지가 붙은 충돌 표, \State와 \Procedure를 쓰는 법, 키워드를 직접 다시 선언하는 방법, 그리고 지금 시작하는 사람을 위한 권장안을 차례로 다룹니다.
두 층 구조: 바깥틀을 만드는 패키지와 내용을 쓰는 패키지
의사코드 조판은 역할이 다른 두 층으로 나뉩니다. 첫 번째 층은 바깥틀 입니다. figure나 table처럼 지면 위를 떠다니고, “Algorithm 1” 이라는 번호 붙은 캡션을 가지며, 상호 참조할 수 있고, 필요하면 목록까지 만들 수 있는 상자입니다. 이를 제공하는 것이 algorithm 이며, 내부에서는 float 패키지의 \newfloat를 불러 새 부동체를 만듭니다(algorithm.sty 31행에 \RequirePackage{float}, 82~94행에 \newfloat{algorithm}{htbp}{loa}). 기본 모양은 \floatstyle{ruled}, 곧 위아래에 괘선이 그어지는 그 체재입니다.
두 번째 층은 내용 입니다. \State(한 줄), \While(반복), \If(분기) 같은 명령들이 들여쓰기와 행 번호를 달고 의사코드 자체를 조판합니다. 여기서 선택지가 셋으로 갈립니다. 오래된 algorithmic, 자유도가 높은 후계 algorithmicx(실무에서는 그 위에 얹힌 배치인 algpseudocode 를 불러옵니다), 그리고 독립된 별세계인 algorithm2e 입니다. 헷갈리는 것은 이름만이 아닙니다. algpseudocode를 불러와도 본문에서 쓰는 환경 이름은 algorithmic 입니다. 패키지 이름과 환경 이름이 어긋나 있습니다.
| 패키지 | 층 | 역할 |
|---|---|---|
algorithm | 바깥틀 | float 위에 \newfloat로 만든 부동체. \caption, \label, \listofalgorithms를 담당합니다. 내용 패키지와 짝지어 씁니다 |
algorithmic | 내용(구) | 최초의 의사코드 환경. 명령이 모두 대문자(\STATE)이고 사용자 정의가 거의 불가능합니다. algorithm과 같은 algorithms 번들에 들어 있습니다 |
algpseudocode | 내용(현행) | algorithmicx 위에 얹히는 표준 배치. 명령은 첫 글자만 대문자(\State)이고 환경 이름은 여전히 algorithmic입니다. algorithmicx는 자동으로 읽힙니다 |
algorithm2e | 둘 다 | 바깥틀과 내용을 스스로 갖춘 독립된 세계. \KwIn, \eIf, 줄 끝의 \\; 라는 독자 문법을 씁니다. 단독으로 사용합니다 |
어떤 조합이 깨지는가: TeX Live 2024에서 전부 시험함
결론부터 말하면 안전한 조합은 algorithm + algpseudocode 하나뿐 입니다. 나머지 조합은 세 가지 방식으로 깨집니다. 실제로 돌려서 얻은 메시지를 그대로 나열합니다.
% SAFE — the intended pairing, compiles cleanly
\usepackage{algorithm}\usepackage{algpseudocode}
% two body packages: the environment name collides
\usepackage{algorithmic}\usepackage{algpseudocode}
! LaTeX Error: Command \algorithmic already defined.
% algorithm2e already owns a container — order changes only which name trips first
\usepackage{algorithm2e}\usepackage{algorithm}
! LaTeX Error: Command \listofalgorithms already defined.
\usepackage{algorithm}\usepackage{algorithm2e}
! LaTeX Error: Command \algorithm already defined.
! LaTeX Error: Command \algorithm* already defined.여기까지는 알아보기 쉬운 실패입니다. 이름이 부딪히는 순간 LaTeX이 멈춰 주므로 로그 첫 줄만 읽으면 원인을 짚을 수 있습니다. 문제는 네 번째 조합, algorithm2e와 algpseudocode 입니다. 두 순서 모두로 시험했지만 읽어들일 때 오류는 0건이었습니다. 게다가 각 환경을 얌전히 쓰기만 하면 둘 다 동작하는 것처럼 보입니다. 그러나 algorithmic 환경 안에서 \For를 쓰는 순간 무너집니다. 두 패키지가 \For와 \If라는 같은 이름을, 오류를 내지 않는 방식으로 정의하고 있기 때문입니다.
\usepackage{algorithm2e}
\usepackage{algpseudocode} % loads fine. no error. no warning.
...
\begin{algorithmic}[1]
\State $x \gets 0$
\For{$i=1$ to $n$} \State $x \gets x+i$ \EndFor
\end{algorithmic}
% output is scrambled — lines merge and reorder — and only then:
! Missing number, treated as zero.
<to be read again> \ALG@b@2@EndFor@0
l.9 \EndFor오류는 \EndFor 줄에서, 그것도 “수가 없다”는, 원인에서 가장 먼 말투로 나옵니다. 프리앰블의 두 줄이 범인이라고는 읽어 낼 수 없습니다. 이것이 네 조합 가운데 최악인 이유 입니다. 또한 이것은 \usepackage의 옵션이 어긋날 때 생기는 “옵션 충돌”과는 다른 실패 입니다. 옵션 충돌은 “두 번째 로드가 첫 번째에 없던 옵션을 요구할 때 발동한다”는 부분집합 판정으로 일어나며, Option clash for package 라는 전용 메시지가 나옵니다. 여기서 일어나는 것은 명령 이름의 중복, 곧 두 패키지가 같은 \For를 정의했다는 것뿐입니다.
algpseudocode 쓰는 법: \State부터 \Procedure까지
의사코드는 \begin{algorithmic} … \end{algorithmic} 안에 쓰며, 생략 가능한 인수로 행 번호를 제어 합니다. [0]은 번호 없음, [1]은 모든 줄에 번호, [n]은 n줄마다 번호입니다. 핵심은 \State로, 대입이나 프로시저 호출처럼 한 문장에 하나씩 둡니다. \While이나 \If처럼 블록을 여는 명령 앞에는 \State를 두지 않습니다(그 자체가 새 줄을 시작합니다). 블록의 내용은 자동으로 들여쓰기되고, 소스 쪽 공백은 출력에 영향을 주지 않습니다. 명령이 첫 글자만 대문자(\State)인 것은, 모두 대문자였던 옛 algorithmic(\STATE, \WHILE)과 구별하는 가장 빠른 단서입니다.
\State— 한 문장(한 줄)의 시작.\State $x \gets 1$처럼 씁니다.\For{cond}…\EndFor— 반복. 출력은 “for … do”로 시작해 “end for”로 닫힙니다.\ForAll{cond}도 있습니다.\While{cond}…\EndWhile— “while … do” / “end while”.\Repeat…\Until{cond}도 쓸 수 있습니다.\If{cond}…\ElsIf{cond}…\Else…\EndIf— 분기. “if … then”, “else if … then”, “else”, “end if”.\ElsIf와\Else는 생략할 수 있습니다.\Procedure{name}{args}…\EndProcedure— 프로시저. “procedure name(args)” / “end procedure”. 함수형\Function{name}{args}…\EndFunction도 같은 모양입니다.\Return— 반환값. 굵은 “return” 뒤에 값이 나옵니다.\Comment{...}— 줄 끝 주석. 오른쪽을 향한 삼각형 ▷ 뒤에 놓입니다.\Require/\Ensure— 사전 조건과 사후 조건. 각각 굵은 “Require:”, “Ensure:”가 앞에 붙습니다.
\documentclass{article}
\usepackage{algorithm}
\usepackage{algpseudocode}
% declare your own keywords: Require/Ensure become Input/Output
\renewcommand{\algorithmicrequire}{\textbf{Input:}}
\renewcommand{\algorithmicensure}{\textbf{Output:}}
\begin{document}
\listofalgorithms
\begin{algorithm}
\caption{Power}\label{alg:p}
\begin{algorithmic}[1]
\Require $n \ge 0$
\Ensure $y = x^n$
\Procedure{Power}{$x, n$}
\State $y \gets 1$
\While{$n \neq 0$}
\State $y \gets y \times x$ \Comment{one step}
\State $n \gets n - 1$
\EndWhile
\State \Return $y$
\EndProcedure
\end{algorithmic}
\end{algorithm}
See Algorithm~\ref{alg:p}.
\end{document}이것을 두 번 컴파일하면 위아래에 괘선을 두른 상자가 부동체로 놓이고, 위쪽에 “Algorithm 1 Power”가 들어갑니다. 왼쪽 끝에는 1부터 8까지 행 번호가 늘어서고, \Require/\Ensure 줄은 재정의한 대로 “Input:”, “Output:”으로 나옵니다. 키워드는 장식이 아니라 바꿔 끼울 수 있는 선언 입니다. 같은 요령으로 \algorithmicwhile, \algorithmicdo, \algorithmicend 등을 고쳐 쓰면 반복과 분기의 단어도 자유롭게 바꿀 수 있습니다. 첫머리의 \listofalgorithms는 “List of Algorithms” 목록을 만들고(algorithm.sty가 loa 확장자의 목차 파일을 씁니다), 본문의 \ref{alg:p}는 “See Algorithm 1.”로 풀립니다. 번호와 상호 참조가 자리 잡도록 반드시 두 번 돌리세요.
바깥틀에 관해 널리 오해되는 이야기가 하나 있어 바로잡아 둡니다. \begin{algorithm}[H]라고 써서 상자를 그 자리에 고정 하고 싶을 때, float 패키지를 직접 불러올 필요는 없습니다. algorithm.sty 31행이 \RequirePackage{float}이므로 algorithm을 불러온 시점에 float는 이미 들어와 있습니다. TeX Live 2024에서 \usepackage{algorithm}만 쓴 문서에 [H]를 넣어, 앞뒤 본문 사이에 상자가 고정되는 것을 확인했습니다. 덧붙여 algorithm 자체가 받는 옵션 plain, ruled, boxed로 틀의 체재를 고를 수 있습니다(기본값은 ruled).
algorithm2e: 중괄호로 블록을 넘기고 줄 끝에 \\;를 찍는 세계
또 하나의 큰 선택지가 algorithm2e 입니다. 바깥틀과 내용을 한 패키지로 해결하는 자기 완결형이며, 프리앰블에서 \usepackage[…]{algorithm2e}로 불러옵니다. algorithm 환경 자체가 부동체이므로 안에 겹쳐 넣을 환경이 없습니다. 문법도 algpseudocode와 크게 달라 구별할 점이 셋입니다. 첫째, 입출력은 전용 명령 \KwIn{…}/\KwOut{…}(또는 \KwData{…}/\KwResult{…})로 씁니다. 둘째, 분기와 반복은 내용을 중괄호 인수로 넘깁니다. \eIf{cond}{then 쪽}{else 쪽}(e는 “else 있음”의 뜻), \For{cond}{body}, \While{cond}{body} 입니다. 셋째, 각 문장 끝에 \\;가 필요합니다. 잊으면 다음 문장이 같은 줄로 흘러듭니다.
\documentclass{article}
\usepackage[ruled, vlined, linesnumbered]{algorithm2e}
\SetKwInOut{Param}{Parameters} % declare a keyword of your own
\DontPrintSemicolon % hide the line-ending \;
\begin{document}
\begin{algorithm}[H]
\caption{Sum of positive entries}\label{alg:s}
\KwIn{an array $a[1..n]$}
\Param{tolerance $\epsilon$}
\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]$\tcp{keep it}
}{
\tcc{skip}
}
}
\Return $s$\;
\end{algorithm}
See Algorithm~\ref{alg:s}.
\end{document}ruled는 위아래에 괘선과 캡션 줄을, vlined는 블록 구조를 나타내는 세로선을(본문에서 같은 일을 하는 명령이 \SetAlgoLined이고 옛 이름은 \SetLine입니다), linesnumbered는 각 줄의 번호를 냅니다. \DontPrintSemicolon은 줄 끝의 \\;를 출력에서 감추는 지정으로, 의사코드가 프로그래밍 언어처럼 보이지 않게 하고 싶을 때 씁니다. 주석은 \tcp{…}(// 형식, 줄 끝에 붙음)와 \tcc{…}(/* … */ 형식, 독립된 줄이 됨) 두 가지입니다. 그리고 \SetKwInOut{Param}{Parameters}가 바로 이 페이지의 주제 입니다. \KwIn이나 \KwOut은 특별한 존재가 아니라 같은 선언 장치로 만들어져 있고, 직접 하나 더 늘릴 수 있습니다. 마찬가지로 \SetKw, \SetKwFunction, \SetKwData가 있으며 \KwTo나 \Return의 단어도 바꿀 수 있습니다. [H]는 algorithm2e가 스스로 구현하므로 여기서도 float를 명시적으로 불러올 필요가 없습니다.
지금 시작한다면 어느 쪽을 고를까
권장안은 algorithm + algpseudocode 입니다. 취향이 아니라 세 가지 실무적 사실 때문입니다. 첫째, TeX Live 2024에서 충돌 없이 통과하는 조합은 이것뿐입니다. 둘째, 바깥틀이 float 위의 평범한 부동체이므로 \caption, \label, \ref, \listofalgorithms가 그림·표와 완전히 같은 방식으로 동작해 새로 익힐 것이 없습니다. 셋째, \State 하나에 문장 하나라는 표기는 투고처 템플릿이 이미 전제하고 있는 경우가 많은 스타일입니다.
algorithm2e를 고를 이유도 분명합니다. 입출력을 명시적으로 선언하고 싶다, 블록을 중괄호로 넘기는 문법과 세로선 배치가 마음에 든다, 키워드를 대량으로 직접 만들고 싶다——이 가운데 하나라도 해당한다면 이쪽이 더 자연스럽습니다. 어느 쪽을 고르든 원칙은 하나입니다. 내용 패키지는 문서 전체에서 하나로 통일 하고, algorithm2e를 쓴다면 algorithm은 불러오지 않는 것입니다. 둘 다 시험해 보고 싶다면 한 문서 안에서 섞지 말고 별도 파일로 비교하세요. 프리앰블의 두 줄이 \EndFor 줄의 알 수 없는 오류가 되어 돌아옵니다.