Composición de algoritmos

Los paquetes de LaTeX para componer pseudocódigo llevan nombres confusamente parecidos —algorithm, algorithmic, algorithmicx (en la práctica se usa algpseudocode) y algorithm2e— y el primer tropiezo es averiguar cuáles riñen entre sí. Pero la combinación realmente peligrosa no es la que lanza un error. Al probar todos los emparejamientos en TeX Live 2024 resulta que cargar algorithm2e junto con algpseudocode no produce ningún error de carga y corrompe en silencio la salida en cuanto escribes \For. Esta página fija el diseño de dos capas (contenedor y contenido), ofrece una tabla de conflictos con los mensajes de error reproducidos de verdad, cubre cómo escribir \State y \Procedure, muestra cómo redeclarar tú mismo las palabras clave y termina con una recomendación para quien empieza hoy.

Dos capas: el paquete que hace el marco y el que escribe el cuerpo

Componer pseudocódigo se divide en dos capas con tareas distintas. La primera es el contenedor: una caja que flota en la página igual que una figure o una table, lleva un título numerado «Algorithm 1», se puede referenciar e incluso reunir en una lista. La aporta algorithm, que internamente construye un flotante nuevo llamando a \newfloat del paquete float (algorithm.sty línea 31: \RequirePackage{float}; líneas 82–94: \newfloat{algorithm}{htbp}{loa}). Su aspecto por defecto es \floatstyle{ruled}: los conocidos filetes arriba y abajo.

La segunda capa es el contenido: comandos como \State (una línea), \While (un bucle) o \If (una rama) que componen el pseudocódigo en sí, con sangrado y números de línea. Aquí la elección se bifurca en tres: el antiguo algorithmic, su sucesor más flexible algorithmicx (en la práctica se carga la disposición construida sobre él, algpseudocode) y el mundo separado y autónomo de algorithm2e. Lo confuso no son solo los nombres: aun cargando algpseudocode, el entorno se sigue llamando algorithmic. El nombre del paquete y el del entorno no coinciden.

PaqueteCapaFunción
algorithmContenedorUn flotante construido con \newfloat sobre float. Gestiona \caption, \label y \listofalgorithms. Se combina con un paquete de cuerpo
algorithmicCuerpo (antiguo)El entorno de pseudocódigo original; comandos en mayúsculas (\STATE); apenas personalizable. Se distribuye en el mismo lote algorithms que algorithm
algpseudocodeCuerpo (actual)La disposición estándar construida sobre algorithmicx; comandos con inicial mayúscula (\State); el entorno sigue llamándose algorithmic. algorithmicx se carga automáticamente
algorithm2eAmbasUn mundo autónomo con contenedor y cuerpo propios; su propia sintaxis con \KwIn, \eIf y un \\; al final de línea. Úsalo a solas

Qué combinaciones fallan: todos los emparejamientos probados en TeX Live 2024

En corto: solo un emparejamiento es seguro, algorithm + algpseudocode. Los demás fallan de tres maneras distintas. Estos son los mensajes tal como salieron de una ejecución real.

reproduced on TeX Live 2024
% 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.

Esos son los fallos fáciles: LaTeX se detiene en cuanto chocan los nombres, y la primera línea del registro señala la causa. El problema es la cuarta combinación: algorithm2e con algpseudocode. Probada en ambos órdenes, el número de errores de carga fue cero. Peor aún: si simplemente usas cada entorno por separado, los dos parecen funcionar. Todo se viene abajo en cuanto escribes \For dentro de un entorno algorithmic, porque ambos paquetes definen los mismos nombres, \For e \If, de una forma que no provoca ningún error.

the silent one
\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

El error aflora en la línea del \EndFor, y encima con la frase «missing number», casi lo más lejos posible de la causa real. Nada apunta a las dos líneas \usepackage del preámbulo. Eso es lo que la convierte en la peor de las cuatro combinaciones. Conviene notar además que este es un fallo distinto del conflicto de opciones que aparece cuando dos llamadas a \usepackage discrepan en las opciones. Un conflicto de opciones se dispara por una prueba de subconjunto —la segunda carga pide una opción que la primera no tenía— y se anuncia con el mensaje propio Option clash for package. Aquí solo hay un nombre de comando duplicado: dos paquetes definieron el mismo \For.

Escribir un cuerpo algpseudocode: de \State a \Procedure

El pseudocódigo se escribe dentro de \begin{algorithmic}\end{algorithmic}, y su argumento opcional controla la numeración de líneas: [0] ninguna, [1] numera todas, [n] una de cada n. La pieza clave es \State, colocada una vez por instrucción: una asignación, una llamada a procedimiento. No se pone \State delante de un comando que abre bloque como \While o \If, que ya empiezan línea por su cuenta. El contenido de los bloques se sangra automáticamente y los espacios de la fuente no afectan a la salida. La inicial mayúscula de los comandos (\State) es la pista más rápida para distinguir esto del antiguo algorithmic en mayúsculas (\STATE, \WHILE).

  • \State — el comienzo de una instrucción (una línea); se usa como \State $x \gets 1$.
  • \For{cond}\EndFor — un bucle; la salida abre con «fordo» y cierra con «end for». También existe \ForAll{cond}.
  • \While{cond}\EndWhile — «whiledo» / «end while». También está disponible \Repeat\Until{cond}.
  • \If{cond}\ElsIf{cond}\Else\EndIf — una rama; «ifthen», «else ifthen», «else», «end if». \ElsIf y \Else son opcionales.
  • \Procedure{name}{args}\EndProcedure — un procedimiento; «procedure name(args)» / «end procedure». La forma función \Function{name}{args}\EndFunction tiene la misma estructura.
  • \Return — un valor de retorno, compuesto como un «return» en negrita seguido del valor.
  • \Comment{...} — un comentario de fin de línea, colocado tras un triángulo que apunta a la derecha ▷.
  • \Require / \Ensure — precondiciones y postcondiciones, cada una precedida de un «Require:» / «Ensure:» en negrita.
document.tex
\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}

Compílalo dos veces y obtendrás una caja flotante con filetes arriba y abajo, encabezada «Algorithm 1 Power». Los números de línea del 1 al 8 bajan por el margen izquierdo, y las líneas \Require/\Ensure salen como «Input:» y «Output:» exactamente como se redefinió: las palabras clave no son adorno, sino declaraciones que puedes sustituir. Reescribe igual \algorithmicwhile, \algorithmicdo, \algorithmicend y las demás, y las palabras de bucles y ramas cambian con ellas. El \listofalgorithms del principio produce una «List of Algorithms» (algorithm.sty mantiene un archivo de índice con la extensión loa), y \ref{alg:p} en el texto se resuelve como «See Algorithm 1.». Compila siempre dos veces para que números y referencias cruzadas se asienten.

Hay un punto sobre el contenedor que se malentiende a menudo; corrijámoslo aquí. Cuando escribes \begin{algorithm}[H] para fijar la caja en su sitio, no hace falta cargar tú mismo el paquete float. La línea 31 de algorithm.sty es \RequirePackage{float}, así que float ya está ahí en cuanto cargas algorithm. En TeX Live 2024 se comprobó que un documento con solo \usepackage{algorithm} y una [H] mantiene la caja fija entre los párrafos circundantes. De paso: algorithm acepta las opciones plain, ruled y boxed para elegir el estilo del marco (por defecto ruled).

algorithm2e: bloques entre llaves y el \\; que no debes olvidar

La otra gran opción es algorithm2e. Es autónoma, aporta contenedor y contenido en un solo paquete y se carga en el preámbulo como \usepackage[…]{algorithm2e}. Su entorno algorithm es el flotante, así que no hay entorno interior que anidar. La sintaxis también difiere claramente de algpseudocode en tres puntos. Primero, la entrada y la salida usan los comandos propios \KwIn{…} y \KwOut{…} (o \KwData{…} y \KwResult{…}). Segundo, las ramas y los bucles reciben su cuerpo como argumento entre llaves: \eIf{cond}{parte then}{parte else} (la e significa «con else»), \For{cond}{body}, \While{cond}{body}. Tercero, cada instrucción debe terminar en \\;: si lo olvidas, la siguiente instrucción se desliza a la misma línea.

document.tex
\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 traza filetes arriba y abajo con una línea de título, vlined añade las líneas verticales que marcan la estructura de bloques (el comando que hace lo mismo desde el cuerpo es \SetAlgoLined, antes llamado \SetLine), y linesnumbered numera cada línea. \DontPrintSemicolon oculta en la salida el \\; de fin de línea, útil cuando no quieres que el pseudocódigo parezca un lenguaje de programación. Los comentarios tienen dos formas: \tcp{…} (estilo //, al final de la línea) y \tcc{…} (estilo /* … */, en una línea propia). Y \SetKwInOut{Param}{Parameters} es justo el tema de esta página: \KwIn y \KwOut no son nada especial, se fabrican con el mismo mecanismo de declaración y puedes añadir otro tú mismo. \SetKw, \SetKwFunction y \SetKwData hacen lo propio con otras clases de palabra clave, y las palabras tras \KwTo y \Return también se pueden sustituir. [H] lo implementa el propio algorithm2e, así que tampoco aquí hace falta cargar float explícitamente.

Cuál elegir si empiezas hoy

La recomendación es algorithm + algpseudocode, y no por gusto sino por tres razones prácticas. Primero, es el único emparejamiento que compila sin conflicto en TeX Live 2024. Segundo, como el contenedor es un flotante ordinario construido sobre float, \caption, \label, \ref y \listofalgorithms se comportan exactamente igual que con figuras y tablas: no hay nada nuevo que aprender. Tercero, el estilo de una instrucción por \State es el que ya presuponen la mayoría de las plantillas de revistas y congresos.

Las razones para elegir algorithm2e son igual de claras: quieres declarar entradas y salidas de forma explícita, te gusta pasar los bloques entre llaves y la disposición con líneas verticales, o piensas inventar muchas palabras clave propias. Si algo de eso encaja, es la opción más natural. Elijas lo que elijas, una regla se mantiene: fija un único paquete de cuerpo para todo el documento y, si usas algorithm2e, no cargues algorithm. Si quieres probar ambos, compáralos en archivos separados en vez de mezclarlos: dos líneas del preámbulo te volverían como un error incomprensible en la línea del \EndFor.