Computable problems and the Halting problem
Non-computable problems, the Halting problem and why it cannot be solved, and what step limits can and cannot tell you.
Do this lesson in the simulatorThe last lesson's limit was time: intractable problems can be solved, just not quickly. This lesson's limit is harder. Some problems cannot be solved by any algorithm, on any computer, however fast, given as long as you like. In 1936 Alan Turing proved that one of the most useful problems imaginable is like this.
Computable and non-computable
A problem is computable (or decidable) if there is an algorithm that, for every possible input, finishes after a finite number of steps with the correct answer. Sorting a list is computable. So is the travelling salesman problem: brute force always finishes, eventually.
A problem is non-computable if no such algorithm can exist. This is not a statement about today's computers or today's programmers. It is proved mathematically that no algorithm can ever solve every case.
| Tractable | Intractable | Non-computable | |
|---|---|---|---|
| Algorithm exists? | yes | yes | no |
| Finishes in reasonable time? | yes, polynomial time | only for small inputs | never, for some inputs |
| Example | sorting | travelling salesman | the Halting problem |
Lesson A6.3 gave a hint that non-computable problems must exist. Every program is a finite string of characters, so the set of all programs is countably infinite. The set of all yes or no problems about whole numbers is so much bigger that it is not countable. There are not enough programs to go round.
The Halting problem
Some programs finish (they halt); some run forever. A loop like while x != 0: x = x - 4 halts when x starts at 8 but never when it starts at 10. It would be extremely useful to have a checker that could look at any program and its input and say, without running it forever, whether it will halt. Every infinite-loop bug would be caught before the program shipped.
The Halting problem: is it possible, in general, to write a program that can tell, given any program and any input, whether that program will halt when run with that input, without running it?
Turing proved the answer is no. The Halting problem is non-computable.
Why no halting checker can exist
The proof is by contradiction. Suppose someone has written a function halts(program, data) that always returns True if program would halt when given data, and False if it would run forever. Programs are just text, so a program can be given to itself as its data. Now write this:
def halts(program, data):
# suppose this always answers correctly, and always finishes
...
def contrary(program):
if halts(program, program):
while True: # halts says it halts, so loop forever
pass
else:
return # halts says it loops, so stop at once
What happens when contrary is given its own code, contrary(contrary)?
- If
halts(contrary, contrary)returnsTrue, it is claimingcontrary(contrary)halts. But thencontraryloops forever. The answer was wrong. - If it returns
False, it is claimingcontrary(contrary)runs forever. But thencontraryreturns at once. Wrong again.
Either way halts gives the wrong answer for this input, so a correct halts cannot exist. Nothing about the proof depends on how clever halts is or how fast the computer is.
What it means in practice
- Some problems cannot be solved by computer at all. The Halting problem was the first shown, and many others follow from it: for example, whether two programs always give the same output, or whether a program ever reaches a particular line.
- Tools have to approximate. A compiler that warns "this code is unreachable" or an analyser that flags a possible infinite loop is right for many cases, but no tool can be right for all of them. They answer "yes", "no" or "cannot tell".
- Running a program is not a way round it. Running a program for a while can prove it halts, when it does. It can never prove it runs forever: it might stop on the very next step.
That last point is how BugBot's simulator deals with it. Every task has a time limit, and a program still going at the end is stopped. The simulator does not decide that the program would never have finished; it just stops waiting.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
def steps_to_one(n, limit):
# the Collatz rule: halve if even, otherwise 3n + 1; stop at 1
steps = 0
while n != 1:
if steps == limit:
return None
n = n // 2 if n % 2 == 0 else 3 * n + 1
steps = steps + 1
return steps
for start in [6, 7, 27]:
print(start, steps_to_one(start, 1000))
Every starting number anyone has ever tried reaches 1 (27 takes 111 steps), but nobody has proved that all of them do. A step limit gives a real answer for the ones that finish and an honest "don't know" otherwise.
Task: the watchdog
Write a watchdog that runs simple programs with a step limit and reports honestly on each.
- Each program in
programsis a tuple(name, x, step, done):nameis a string,xis the starting whole number,step(x)returns the next value ofx, anddone(x)returnsTruewhen the program has halted. - Write
watch(name, x, step, done, limit). Before each step, checkdone(x). If it isTrue, print<name>: halted after <n> steps, wherenis the number of steps taken so far, and stop watching. Otherwise take a step. Iflimitsteps have been taken anddone(x)is still notTrue, print<name>: no answer after <limit> steps. - Call
watchfor each program in order, withlimitset to 1000.
Three lines in all, such as p1: halted after 3 steps. Work the counts out by running the programs, not by hand.
# the two lines every program starts with: the commands, then the robot
from bugbot import *
connect()
programs = [
("p1", 10, lambda x: x - 3, lambda x: x == 1),
("p2", 10, lambda x: x - 4, lambda x: x == 0),
("p3", 27, lambda x: x // 2 if x % 2 == 0 else 3 * x + 1, lambda x: x == 1),
]
def watch(name, x, step, done, limit):
pass
Challenges
- Program p2 never halts. Prove it by thinking about the values
xtakes, not by running it. - A program whose
xcan only take a few different values must either halt or repeat a value. Add a check towatchthat printsloops foreverwhen a value repeats. Why does this not solve the Halting problem? - Explain the difference between "intractable" and "non-computable" to someone who thinks a faster computer fixes both.