The halting problem, explained with Turing’s proof
The halting problem asks for a program that reads any program and its input and says whether it will eventually stop or run forever. Alan Turing proved in 1936 that no such program can exist. The proof fits in a few lines, and its consequences reach every compiler, linter and verification tool written since.
On this page
The problem
Some programs obviously stop: print("hi"). Some obviously don’t: while true: pass. Many are hard to judge by eye. Here is a short one:
def collatz(n):
while n != 1:
n = n // 2 if n % 2 == 0 else 3 * n + 1Started at 27 it stops after 111 steps, and at 97 after 118. Every starting value ever tried stops, but nobody has proved that every one does: whether this four-line loop halts for all positive is the Collatz conjecture, open since 1937. A general halting tester would settle it, and countless other open problems, at once.
Given the source code of a program and an input , decide whether eventually stops when run on .
A decider for it would be a program that always stops and answers correctly: if stops, and if it runs forever.
The claim is not that halting is hard to decide, or slow. It is that no program at all, however clever and however much time it is allowed for each answer, gives the right answer for every program and input. Problems like this are called undecidable.
Turing’s proof, step by step
The proof is by contradiction: assume a decider exists, and use it to build a program that it must get wrong.
Theorem: the halting problem is undecidable
No program decides, for every program and input , whether halts.
- Suppose, for contradiction, that exists: it always stops, and answers “halts” or “loops” correctly.
- Programs are text, so a program can be given its own source code as input. Using as a subroutine, write this program :
asks whether halts when fed itself, then does the opposite.def D(P): if H(P, P) == "halts": while True: pass # loop forever else: return # stop at once - Now run on its own source code. must return one of its two answers.
- If says “halts”, then takes the first branch and loops forever. So does not halt, and was wrong.
- If says “loops”, then takes the second branch and stops. So halts, and was wrong again.
- Either way gives a wrong answer on the input , contradicting the assumption that it is always right. So no such exists.
Nothing in the argument depends on how works inside. Whatever program you propose as a halting decider, the construction produces a specific input on which it fails.
Why it is called a diagonal argument
List every program as (programs are finite strings, so they can be listed). Make a table whose cell in row , column says whether halts on input . The program reads the diagonal and flips it: on input it does the opposite of what does on . So differs from every at the -th column and cannot be any row of the table. But is a program, so it must be a row. The contradiction shows the table cannot be computed. This is the same trick Cantor used in 1891 to show that the real numbers cannot be listed.
What “program” means here
Turing made “program” precise with his machines: a finite control reading and writing symbols on an unbounded tape. The result holds for any model of computation that is as powerful, which includes every general-purpose programming language with unbounded memory. By the Church–Turing thesis, anything we would call an algorithm can be written as such a machine, so the halting problem has no algorithmic solution, full stop.
The unbounded memory matters. A finite-state machine, or a real computer with a fixed amount of memory, has finitely many configurations. Run it long enough and it either stops or repeats a configuration, after which it loops forever. So halting is decidable for finite machines in principle; the catch is that a machine with a gigabyte of memory has configurations, and waiting for a repeat is hopeless in practice.
Rice’s theorem: almost everything is undecidable
Once one problem is known to be undecidable, others follow by reduction: if a decider for problem could be used to decide halting, then is undecidable too. For example, “does ever print the word done?” is undecidable: given and , build a program that runs silently and then prints done. It prints done exactly when halts.
So no algorithm decides, for all programs, whether a program returns 0 on every input, whether two programs compute the same function, whether a program ever divides by zero, or whether it is a virus. Properties of the text, such as “does the source contain a loop?” or “does it use more than 100 lines?”, are decidable: they are not about behaviour.
What this means for real tools
Undecidability does not make program analysis useless. It means every analysis has to give something up, and good tools choose carefully what:
- Over-approximate. A type checker or static analyser may reject some correct programs (false alarms) so that it never accepts an incorrect one. When a compiler warns that a variable “may be used uninitialised” in code you know is fine, this is why.
- Under-approximate. Testing and bug-finding tools report only real problems but can miss some.
- Restrict the language. Total languages, used in proof assistants, only allow recursion that provably terminates, so every program halts. They give up some programs to get the guarantee.
- Answer “don’t know”. Termination provers prove that many real programs halt, and give up on the rest. A time-out is an honest third answer.
Common misconceptions
- “So we can never tell whether a program halts.”
- For a particular program we often can, by proof. The theorem only says no single algorithm works for all programs.
- “Just run it and see.”
- Running it answers “halts” when it halts, but if it hasn’t stopped yet you never learn whether it will. This gives a semi-decision procedure: the halting set is recognisable (computably enumerable) but not decidable. Its complement, “runs forever”, is not even recognisable.
- “A faster computer or quantum computer would help.”
- No. The proof makes no assumption about speed. Quantum computers compute the same functions as classical ones, just sometimes faster.
- “The proof is a trick about self-reference that never matters in practice.”
- Self-reference is only the tool for the proof. Through reductions, the same limit applies to ordinary questions about ordinary programs, as Rice’s theorem shows.
Related results
- Gödel’s incompleteness theorem (1931) is a close cousin: any consistent formal system strong enough for arithmetic has true statements it cannot prove. If it could prove every true “ does not halt” statement, we could decide halting by searching for proofs.
- The Busy Beaver function, the most steps any halting -state Turing machine can take, grows faster than any computable function, because computing it would solve the halting problem.
- Hilbert’s tenth problem, whether a polynomial equation has integer solutions, was shown undecidable in 1970 by reducing halting to it.
References
- A. M. Turing, “On computable numbers, with an application to the Entscheidungsproblem”, Proceedings of the London Mathematical Society s2-42(1), 1937 (read 1936). doi:10.1112/plms/s2-42.1.230
- H. G. Rice, “Classes of recursively enumerable sets and their decision problems”, Transactions of the American Mathematical Society 74(2), 1953. doi:10.1090/S0002-9947-1953-0053041-6
- M. Sipser, Introduction to the Theory of Computation, 3rd ed., Cengage, 2012. Chapters 4 and 5.
- “Halting problem”, Wikipedia.