Atlasingeniería

Teoría de la computaciónComputabilidadTema 3

El problema de la parada

No existe un programa que, mirando otro programa, decida siempre si va a terminar. La demostración cabe en cinco líneas y consiste en construir el programa que se contradice a sí mismo.

Para este tema conviene tener claro:Máquina de Turing

Sería utilísimo: una herramienta que lea tu código y avise si ese ciclo termina o se cuelga para siempre. No es que todavía no se haya construido, ni que sea muy difícil. Se puede demostrar que no puede existir, y la demostración es corta.

Qué se está pidiendo, exactamente

El problema es este: dado un programa PP y una entrada xx, decidir si PP ejecutado sobre xx termina. Se pide un procedimiento que siempre responda, y que siempre acierte.

Conviene notar por qué no alcanza con ejecutarlo y ver qué pasa: si termina, se sabe; pero si todavía no terminó, no hay forma de distinguir “va a terminar en un rato” de “no termina nunca”. Esa asimetría es la pista de que el problema es reconocible pero no decidible.

Antes de seguir, predecí

Si tuvieras una computadora infinitamente rápida, ¿podrías resolver el problema de la parada?

La demostración, por contradicción

La demostración es por contradicción. Supongamos que existe termina(P, x), que devuelve verdadero o falso y siempre acierta. Con ella construimos un programa nuevo:

diagonal(P):
    si termina(P, P):
        ciclar para siempre
    si no:
        terminar

Ahora la pregunta: ¿qué hace diagonal(diagonal)? Si termina dice que sí, el programa cicla para siempre, así que no termina. Si dice que no, termina de inmediato. En los dos casos termina se equivoca, y había supuesto que nunca lo hacía. Por lo tanto no existe.

Listemos todos los programas, uno por fila, y démosle a cada uno como entrada el código de cada programa. Cada casilla dice si termina o cicla.

1 / 6
La tabla es el argumento entero. Cada fila es un programa, cada columna es una entrada, y la fila nueva se construye para diferir de cada fila vieja justo en la casilla de la diagonal. Por eso no puede ser ninguna de ellas, y sin embargo la habríamos escrito nosotros.

La diagonalización, el mismo truco de Cantor

El truco es el mismo que usó Cantor para probar que los reales son incontables y Gödel para sus teoremas de incompletitud: la diagonalización. Se construye un objeto que difiere de cada elemento de una lista supuestamente completa, justo en el lugar que le corresponde.

Acá ese objeto es un programa que hace exactamente lo contrario de lo que se predice sobre él. La autorreferencia no es un juego de palabras: es posible porque un programa se puede pasar como dato a otro, que es la propiedad de la máquina universal.

Lo que el resultado no dice

Vale la pena ser preciso sobre el alcance, porque se exagera seguido. El resultado no dice que no se pueda analizar ningún programa: para muchísimos casos concretos se puede probar perfectamente bien si terminan.

Lo que no existe es el procedimiento general y siempre correcto. Un analizador puede responder “termina”, “no termina” o “no sé”, y ser utilísimo. Toda la industria del análisis estático vive en ese margen: los verificadores aceptan responder “no sé”, o restringen el lenguaje a construcciones donde la terminación sí es decidible.

Todo lo que se cae con él

Las consecuencias se propagan. Por reducción, se prueba que tampoco es decidible si dos programas calculan lo mismo, si un programa alguna vez escribe en cierta variable, o si un fragmento de código es alcanzable.

El teorema de Rice generaliza el golpe: toda propiedad no trivial del comportamiento de un programa es indecidible. No de su texto —contar líneas es decidible— sino de lo que computa. Un detector perfecto de virus, entendido como “este programa hace algo malicioso”, cae bajo esa prohibición.

Las tres formas de convivir con esto

Con eso se convive de tres formas. Aproximar: aceptar falsos positivos, como hace un type checker que rechaza programas correctos pero que no puede verificar. Restringir: usar lenguajes donde toda función termina por construcción, como los asistentes de demostración. O acotar: ejecutar con un límite de pasos y responder “no terminó en el presupuesto”.

Las tres son formas de cambiar la pregunta por una decidible. Ninguna resuelve la original, porque la original no tiene solución.

Lo que sí se puede hacer

Pregunta¿Decidible?Qué se hace en la práctica
¿Este programa arbitrario termina?nolímite de tiempo y matarlo
¿Este ciclo con cota explícita termina?el compilador lo verifica
¿Esta función recursiva sobre un tipo finito termina?verificadores de terminación
¿Hay una división por cero en esta línea?no en generalanálisis estático conservador
¿Este programa es seguro?no en generalanalizadores que avisan de más
Las filas decidibles muestran la salida real: restringir el lenguaje. Si el programa sólo puede recursar sobre estructuras que se achican, la terminación se puede verificar, y eso es lo que hacen los asistentes de demostración.
Más a fondo · formalReconocible pero no decidible

El problema de la parada es reconocible: hay un procedimiento que dice que sí cuando la respuesta es sí —ejecutar el programa y esperar—. Lo que no hay es uno que diga que no.

Su complemento —«este programa no termina»— ni siquiera es reconocible, y eso da una caracterización linda: un lenguaje es decidible si y sólo si tanto él como su complemento son reconocibles. Si los dos se pueden reconocer, se corren los dos procedimientos en paralelo y uno de los dos va a contestar.

Esa jerarquía —decidible dentro de reconocible, y afuera lo que no es ni eso— es el mapa de lo incomputable, y explica por qué algunos problemas admiten herramientas parciales útiles y otros no admiten absolutamente nada.

Cierre

No hay decisor general de terminación, y la prueba es construir el programa que contradice cualquier decisor propuesto. El resultado no impide analizar programas concretos: impide el método universal, y por eso las herramientas reales aproximan, restringen o acotan.

Autoevaluación

¿Lo entendiste?

¿Por qué no alcanza con ejecutar el programa y ver qué pasa?
En la demostración, ¿qué hace diagonal(diagonal)?
¿Qué técnica usa la demostración?
¿Significa esto que no se puede analizar código automáticamente?