43. Autómatas y lenguajes formales

Los autómatas finitos son máquinas abstractas que leen símbolos y cambian de estado. Permiten definir lenguajes, validar secuencias, analizar texto y modelar procesos con una cantidad limitada de memoria.

43.1 Introducción

Un autómata es un modelo matemático de computación. Recibe una entrada símbolo por símbolo, conserva un estado interno y sigue reglas de transición hasta decidir si acepta o rechaza la cadena.

Este modelo no pretende representar toda la potencia de una computadora. Su restricción a una cantidad finita de estados lo hace ideal para patrones regulares, validación de formatos, analizadores léxicos y controladores.

43.2 Alfabetos y cadenas

Un alfabeto, normalmente denotado Σ, es un conjunto finito de símbolos. Una cadena es una secuencia finita de símbolos del alfabeto.

Σ = {0, 1}: alfabeto binario.
0101: cadena de longitud 4.

Σ = {a, b, c}: alfabeto de tres símbolos.
abca: cadena válida; abd: no es válida porque d no pertenece a Σ.

La cadena vacía se denota ε y tiene longitud cero. Pertenece a Σ* para cualquier alfabeto, donde Σ* representa el conjunto de todas las cadenas finitas construidas con Σ.

43.3 Lenguajes formales

Un lenguaje formal es cualquier subconjunto de Σ*. No se define por significado humano, sino por las cadenas que contiene.

Sobre Σ = {0, 1}:
L = {cadenas con cantidad par de unos}.

ε, 0, 11 y 1010 pertenecen a L.
1, 101 y 111 no pertenecen a L.

Una gramática, una expresión regular o un autómata son distintas formas de especificar un lenguaje. Los autómatas finitos reconocen una familia importante llamada lenguajes regulares.

43.4 Definición de autómata finito

Un autómata finito determinista se define mediante una quíntupla:

M = (Q, Σ, δ, q0, F).

Q: conjunto finito de estados.
Σ: alfabeto de entrada.
δ: función de transición.
q0: estado inicial.
F: conjunto de estados de aceptación.

La función δ indica a qué estado se pasa al leer cada símbolo. El estado resume toda la información del pasado que el autómata necesita para continuar.

43.5 Aceptación de una cadena

El autómata comienza en q0 y procesa la cadena de izquierda a derecha. Después de consumir todos los símbolos, acepta si el estado actual pertenece a F.

1. Comenzar en q0.
2. Leer el siguiente símbolo.
3. Aplicar δ(estado, símbolo).
4. Repetir hasta terminar la cadena.
5. Aceptar si el estado final está en F.

Una cadena puede recorrer estados aceptadores en el medio y terminar rechazada; lo único decisivo es el estado después de leer toda la entrada.

43.6 Autómata determinista

Un autómata finito determinista, AFD o DFA, tiene exactamente una transición definida para cada combinación de estado y símbolo de entrada.

Determinismo:
δ: Q × Σ → Q.

Desde un estado y un símbolo hay un único siguiente estado.
No hay elecciones ni transiciones vacías en la definición básica de AFD.

El determinismo hace que una cadena tenga un único recorrido posible. Un AFD puede implementarse directamente con una tabla de transiciones o un mapa de estados.

43.7 Ejemplo: paridad de unos

Construyamos un AFD sobre {0, 1} que acepte cadenas con una cantidad par de unos. Solo hace falta recordar si hasta ahora se vio una cantidad par o impar de unos.

Estado actualCon 0Con 1¿Acepta?
parparimpar
imparimparparNo

Leer un cero no cambia la paridad. Leer un uno alterna entre estados. El estado inicial es par porque antes de leer símbolos hay cero unos, y cero es par.

43.8 Recorrer el ejemplo

Para la cadena 1010, el recorrido es: par → impar → impar → par → par. El estado final es par, por lo tanto la cadena se acepta.

Inicio: par.
Leer 1: impar.
Leer 0: impar.
Leer 1: par.
Leer 0: par.

1010 pertenece al lenguaje.

Para 101, el estado final es impar y la cadena se rechaza. El autómata no necesita recordar la posición ni contar todos los unos: dos estados bastan para conservar la paridad.

43.9 Implementar un AFD en JavaScript

function acepta(afd, cadena) {
  let estado = afd.inicial;

  for (const simbolo of cadena) {
    const transiciones = afd.transiciones.get(estado);
    if (!transiciones || !transiciones.has(simbolo)) return false;
    estado = transiciones.get(simbolo);
  }
  return afd.aceptacion.has(estado);
}

const paridadUnos = {
  inicial: "par",
  aceptacion: new Set(["par"]),
  transiciones: new Map([
    ["par", new Map([["0", "par"], ["1", "impar"]])],
    ["impar", new Map([["0", "impar"], ["1", "par"]])]
  ])
};

console.log(acepta(paridadUnos, "1010")); // true
console.log(acepta(paridadUnos, "101"));  // false

En un AFD completo cada estado debe tener transición para todos los símbolos del alfabeto. Si un formato no permite cierto carácter, se puede usar un estado de rechazo explícito o rechazar al no encontrar transición.

43.10 Estado de rechazo o sumidero

Un estado sumidero recibe entradas inválidas y permanece en rechazo para cualquier símbolo posterior. Permite que la función de transición sea total sin perder la decisión de rechazo.

Estado error:
con 0 → error.
con 1 → error.

Una vez alcanzado, no hay forma de aceptar la cadena.

Este estado es útil al convertir una especificación informal en un AFD formal. En una implementación práctica, devolver rechazo inmediato puede ser más directo si no se necesita observar el recorrido completo.

43.11 Autómatas no deterministas

Un autómata finito no determinista, AFN o NFA, puede tener cero, una o varias transiciones para un mismo par de estado y símbolo. También puede incluir transiciones ε que no consumen símbolo.

AFD: desde estado y símbolo hay una sola transición.
AFN: puede haber varias alternativas.

Un AFN acepta si existe al menos un recorrido posible que termina en aceptación.

El no determinismo es una herramienta de descripción, no una capacidad mágica de ejecución. Todo AFN tiene un AFD equivalente, aunque la conversión puede aumentar la cantidad de estados.

43.12 Conversión de AFN a AFD

La construcción por subconjuntos construye estados de un AFD que representan conjuntos de estados posibles del AFN. Con transiciones ε se calcula además la clausura ε de cada conjunto.

Un estado del AFD equivalente representa varios estados del AFN.

Si el AFN tiene n estados, el AFD puede necesitar hasta 2n estados.
En muchos casos reales se alcanzan muchos menos.

La equivalencia entre AFD y AFN muestra que ambos reconocen exactamente los lenguajes regulares. La diferencia principal está en comodidad de diseño y costo de la representación.

43.13 Expresiones regulares

Una expresión regular describe patrones de cadenas mediante concatenación, alternativa y repetición. Para lenguajes regulares, expresiones regulares y autómatas finitos tienen el mismo poder expresivo.

Concatenación: ab.
Alternativa: a|b.
Repetición: a*.

Una expresión puede convertirse en AFN;
un autómata puede convertirse en una expresión equivalente.

Las expresiones regulares son cómodas para patrones compactos. Los autómatas son útiles para visualizar estados, procesar entrada incrementalmente y demostrar propiedades. El próximo tema profundizará en gramáticas y expresiones regulares.

43.14 Analizadores léxicos

Un compilador o intérprete separa el texto fuente en tokens: identificadores, números, palabras reservadas, operadores y espacios. Muchos de esos patrones son regulares y se reconocen con autómatas.

Identificador simple:
letra seguida de letras, dígitos o guiones bajos.

Número entero simple:
uno o más dígitos.

El autómata cambia de estado mientras cada símbolo conserva un prefijo válido.

Reconocer tokens no es lo mismo que validar toda la sintaxis del lenguaje. Las estructuras anidadas, como paréntesis balanceados arbitrariamente, requieren modelos con memoria adicional.

43.15 Validación de formatos

Los AFD son apropiados para validar secuencias con reglas locales: códigos con prefijo, estados de un formulario, protocolos simples y formatos de entrada acotados.

Ejemplo: código con dos letras seguidas de cuatro dígitos.

Estados: inicio → letra1 → letra2 → dígito1 → ... → dígito4.
Solo el estado final acepta.

Una validación real suele sumar límites de longitud, normalización, contexto de negocio y mensajes de error. El autómata modela la regla de secuencia, no todas las políticas del sistema.

43.16 Autómatas como máquinas de estados

Una interfaz, un pedido o un protocolo también puede modelarse con estados y transiciones. La diferencia con un reconocedor formal es que las transiciones pueden ejecutar acciones y las entradas pueden ser eventos complejos.

Pedido: nuevo → pagado → enviado → entregado.

Evento cancelar puede ser válido solo antes de enviar.
El modelo permite declarar transiciones permitidas y prohibidas.

Hacer explícita la máquina de estados evita combinaciones imposibles, facilita las pruebas y ayuda a documentar qué eventos cambian realmente el estado de un proceso.

43.17 Límites de los autómatas finitos

Un autómata finito tiene memoria limitada a su estado, por lo que no puede recordar una cantidad arbitraria. No puede reconocer, por ejemplo, el lenguaje de paréntesis correctamente balanceados con profundidad sin límite.

Para validar ( ... ) anidado se debe recordar cuántos paréntesis abiertos faltan cerrar.

Ese contador no tiene un máximo fijo en la especificación general.
Se necesita una pila: un autómata con pila o un parser.

También escapan a los autómatas finitos lenguajes como {anbn | n ≥ 0}. Conocer estos límites ayuda a no resolver un problema de sintaxis compleja con una expresión regular inadecuada.

43.18 Explosión de estados

Combinar reglas puede multiplicar la cantidad de estados. Si un autómata controla dos propiedades independientes con m y n estados, su producto puede necesitar hasta m·n estados para recordar ambas.

Regla 1: paridad de unos, 2 estados.
Regla 2: terminar en 01, varios estados.

El autómata combinado debe recordar ambas situaciones a la vez.

La minimización de autómatas busca reducir estados equivalentes sin cambiar el lenguaje reconocido. En implementaciones, a veces una tabla clara vale más que una minimización difícil de mantener.

43.19 Diseño de un reconocedor

  1. Definir el alfabeto permitido.
  2. Especificar qué cadenas deben aceptarse y rechazarse.
  3. Identificar la información mínima que debe recordar el estado.
  4. Crear estados para cada situación relevante.
  5. Completar transiciones y marcar estados de aceptación.
  6. Probar cadena vacía, casos límite, símbolos inválidos y casos largos.

El objetivo es que cada estado tenga una interpretación legible. Un diagrama con nombres como esperandoDígito suele ser más fácil de revisar que estados numerados sin significado.

43.20 Aplicaciones

  • Expresiones regulares y validación de entradas.
  • Análisis léxico de lenguajes de programación.
  • Protocolos y comunicaciones con estados definidos.
  • Controladores, interfaces y flujos de negocio.
  • Reconocimiento de secuencias en datos y eventos.
  • Modelos de verificación de sistemas reactivos.

Los autómatas no sustituyen toda la lógica de una aplicación, pero ofrecen una base precisa cuando el comportamiento depende de una secuencia de símbolos o eventos y de un número finito de estados.

43.21 Errores frecuentes

  • Confundir el alfabeto con el lenguaje que se quiere reconocer.
  • Aceptar una cadena al pasar temporalmente por un estado final en lugar de revisar el estado final real.
  • Olvidar transiciones para símbolos permitidos o no definir un estado de rechazo.
  • Creer que un AFN reconoce lenguajes más potentes que un AFD.
  • Usar un autómata finito para contar o anidar sin límite.
  • Implementar una expresión regular compleja sin probar casos límite y entradas inválidas.

43.22 Qué debes recordar y conclusión

  • Un lenguaje formal es un conjunto de cadenas sobre un alfabeto finito.
  • Un AFD se define por estados, alfabeto, transiciones, estado inicial y estados de aceptación.
  • La aceptación depende del estado después de consumir toda la cadena.
  • AFD y AFN reconocen los mismos lenguajes regulares.
  • Los autómatas finitos son útiles para patrones, tokens, protocolos y flujos de estados.
  • La memoria finita impide reconocer estructuras con anidamiento arbitrario.

Los autómatas convierten patrones y secuencias en reglas exactas de transición. En el próximo tema estudiaremos gramáticas y expresiones regulares, dos lenguajes complementarios para describir conjuntos de cadenas.