Il problema della terminazione, noto anche come halting problem in inglese, è uno degli esempi fondamentali che mostrano i limiti della computazione automatica. In termini semplici la domanda è: esiste un algoritmo generale che, preso in input la descrizione di un programma e un suo input, possa sempre dire correttamente se quel programma terminerà o continuerà a eseguire per sempre? La risposta, dimostrata nel 1936 da Alan Turing e collegata ai risultati indipendenti di Alonzo Church, è negativa: non esiste un procedimento meccanico che decida questo problema per tutti i possibili programmi e input.
Definizione informale e concetto formale
Informalmente si può pensare al problema così: date le istruzioni di un programma P e un valore di input x, vogliamo una funzione H(P,x) che risponda "sì" se l'esecuzione di P con input x si arresta in un tempo finito, e "no" se l'esecuzione continua indefinitamente. Un "decisore" per il problema sarebbe un algoritmo che calcola H correttamente per ogni coppia (P,x). In formulazioni formali si usa il modello dei macchine di Turing: il problema dell'arresto è l'insieme di coppie codificate
per cui la macchina P si ferma su x. Questo insieme è ricorsivamente enumerabile (semidecidibile): è possibile simulare passo passo P su x e, se P si arresta, la simulazione finirà e lo rileveremo. Tuttavia non è ricorsivo: non esiste un algoritmo che dia sempre una risposta corretta in tempo finito sia per i casi che si fermano sia per i casi che non si fermano.
Idea della dimostrazione di non decidibilità
La dimostrazione classica utilizza l'autoreferenzialità e un argomento per assurdo. Supponiamo per contraddizione che esista un ipotetico programma H che decide il problema della terminazione: dato (P,x) restituisce "si" se P si arresta su x, altrimenti "no". A partire da H si costruisce un nuovo programma D che, dato in input la descrizione di un programma Q, fa questo: verifica cosa dice H su (Q,Q); se H afferma che Q si arresta quando eseguito sulla sua stessa descrizione, allora D entra in un ciclo infinito; se H dice che Q non si arresta su Q, allora D termina. Ora chiediamoci cosa succede eseguendo D sulla propria descrizione: D(D). Se H dice che D si arresta su D, allora per definizione D entra in loop, contraddicendo la predizione di H; se H dice che D non si arresta su D, allora D termina, di nuovo contraddizione. Poiché entrambe le alternative portano a un assurdo, l'assunto iniziale (l'esistenza di H) è falso. Questo schema è una versione della tecnica diagonalizzante che ricorre anche in altri contesti, come il teorema di Gödel e i risultati di Church.
Conseguenze teoriche e pratiche
Il fatto che il problema della terminazione sia indecidibile ha molte ripercussioni nella teoria della computazione e nelle pratiche di ingegneria del software. Tra le conseguenze teoriche ricordiamo:
- Rice's theorem: molte proprietà non banali del comportamento di programmi (ad esempio, "il programma stampa qualcosa di specifico" o "il programma accetta almeno un input") sono indecidibili;
- la distinzione fra problemi ricorsivamente enumerabili e ricorsivi: il problema dell'arresto è semidecidibile ma non decidibile;
- la gerarchia di indecidibilità: problemi come la totalità (un programma si arresta per ogni input?) sono ancora più complessi dal punto di vista computazionale.
Dal punto di vista pratico, l'indecidibilità non impedisce l'uso di tecniche utili per molti casi concreti. Esistono analizzatori statici, tecniche di prova di terminazione per linguaggi restrittivi, e strumenti di testing che rilevano bug comuni. Tuttavia non esiste un "verificatore universale" in grado di provare la terminazione per ogni programma possibile. Nella pratica industriale si usano approcci approssimati, euristici o interattivi (prove assistite) per affrontare casi rilevanti.
Esempi, varianti e problemi correlati
Alcuni esempi aiutano a chiarire la differenza tra casi semplici e la difficoltà generale. Programmi con cicli il cui numero di iterazioni è determinato da controlli finiti (ad esempio un for che scorre un intervallo noto) sono facili da analizzare: si può provare che terminano. Programmi che dipendono da condizioni non banali o da calcoli ricorsivi possono essere molto difficili da classificare. La macchina di Busy Beaver è un esempio che illustra come quantità associate a macchine limitate possano crescere in modo non computabile: i valori massimi di passi prima dell'arresto per macchine di Turing con n stati non sono computabili in modo generale. Altri problemi strettamente collegati sono:
- il problema dell'equivalenza tra programmi (decidere se due programmi calcolano la stessa funzione), che è anch'esso indecidibile in generale;
- il problema della totalità (un programma termina per ogni input), di livello più alto di indecidibilità;
- problemi di sicurezza come la presenza di vulnerabilità dipendono spesso da proprietà indecidibili nella loro forma più generale.
Origini storiche e notabilità
La formulazione moderna della non decidibilità del problema della terminazione è attribuita a Alan Turing nel 1936, nel contesto più ampio dello studio dei limiti delle macchine meccaniche e della risposta all'Entscheidungsproblem. Parallelamente, Alonzo Church sviluppò un'analisi usando il calcolo lambda che portò a risultati equivalenti. Queste scoperte hanno posto le basi della teoria della computazione, introducendo modelli formali (macchine di Turing, calcolo lambda) e chiarendo che alcuni problemi non possono essere risolti da procedure algoritmiche. Per approfondire il tema e le sue implicazioni in informatica teorica e nella pratica, si possono consultare risorse introduttive sul problema dell'arresto e testi di teoria della computazione.