Computability & Turing Machines
CollegeIn 1936, Alan Turing proposed a precise mathematical model of what it means to "compute" at all: the Turing machine. It consists of an infinite tape divided into cells, a head that reads and writes one cell at a time and can move left or right, and a finite set of states governed by a transition function: given the current state and the symbol under the head, it specifies what symbol to write, which direction to move, and which state to enter next.
Despite its austerity, the Turing machine can compute anything any modern computer can compute — no more, no less. This is the Church–Turing thesis: any function that is "effectively computable" by some intuitive algorithmic process can be computed by a Turing machine. Every real programming language, however different it looks, is provably equivalent in power to this simple tape-and-head device.
A problem is decidable if some Turing machine, given any input, always halts and correctly answers yes or no. A problem is undecidable if no such machine can exist for it — not because we haven't found a clever enough algorithm yet, but because a proof shows none ever can.
The most famous undecidable problem is the halting problem: given a program and an input, will it eventually halt, or run forever? Turing proved no algorithm can decide this in general, using a proof by contradiction (a diagonal argument): suppose a program HALTS(P, x) existed that always correctly answers whether program P halts on input x. Build a new program PARADOX(P) that calls HALTS(P, P) and does the opposite — loops forever if P halts on itself, halts if it doesn't. Now ask: does PARADOX(PARADOX) halt? Either answer contradicts what HALTS just claimed. So HALTS cannot exist.
This isn't a curiosity — it sets a hard ceiling on computation itself. No antivirus can perfectly detect "will this program misbehave," no compiler can perfectly detect "will this loop terminate," for the same fundamental reason: it would have to solve the halting problem.
Big idea. Computability theory asks not "how fast?" but "at all?" — some perfectly well-defined problems have no algorithmic solution, ever, on any computer, no matter how powerful or how much time you allow.
We implement a tiny Turing machine as a general simulator, then run it on a program that increments a binary number by one — a real, if minimal, example of a head moving across a tape following rules.
# A Turing machine: states, a transition table, and a tape (dict: position -> symbol).
# Transition table: (state, symbol) -> (new_symbol, move, new_state) move in {-1, 0, +1}
def run_turing_machine(transitions, start_state, halt_states, tape, head=0, max_steps=10000):
state = start_state
steps = 0
while state not in halt_states and steps < max_steps:
symbol = tape.get(head, "_") # "_" = blank cell
key = (state, symbol)
if key not in transitions:
raise ValueError("no rule for state=%s symbol=%s (machine stuck)" % key)
new_symbol, move, new_state = transitions[key]
tape[head] = new_symbol
head += move
state = new_state
steps += 1
return tape, head, state, steps
# Binary increment: scan right to the end of the number, then add 1 with carries
# moving left, exactly like adding 1 by hand.
transitions = {
("scan_right", "0"): ("0", +1, "scan_right"),
("scan_right", "1"): ("1", +1, "scan_right"),
("scan_right", "_"): ("_", -1, "add_one"), # found the end, start carrying
("add_one", "0"): ("1", 0, "done"), # 0 -> 1, no carry, finished
("add_one", "1"): ("0", -1, "add_one"), # 1 -> 0, carry left
("add_one", "_"): ("1", 0, "done"), # ran off the left: new leading 1
}
# Tape holds the binary number 101 (=5); increment should give 110 (=6)
tape = {0: "1", 1: "0", 2: "1"}
final_tape, head, state, steps = run_turing_machine(transitions, "scan_right", {"done"}, tape)
result = "".join(final_tape.get(i, "_") for i in range(min(final_tape), max(final_tape) + 1))
print(result.strip("_")) # -> "110" (5 + 1 = 6, computed one cell at a time)
Notice how primitive each step is — read a cell, write a cell, move one square, change state — and yet chaining thousands of such steps performs real arithmetic. Computation is not one grand leap of intelligence; it's an enormous number of tiny, mechanical, well-defined steps.
Q1 What does the Church–Turing thesis actually claim, and can it be proven?
It claims that Turing machines capture exactly the informal notion of "effectively computable." It is a thesis, not a theorem — it can't be formally proven because "effectively computable" isn't itself a formal mathematical object. Its support comes from the fact that every seriously proposed alternative model of computation (lambda calculus, RAM machines, real CPUs) has been proven exactly as powerful as a Turing machine, never more.
Q2 Does "undecidable" just mean "we haven't found the algorithm yet"?
No. Undecidable means it has been mathematically proven that no algorithm can ever exist for the problem, for any amount of cleverness or time. This is a much stronger and more permanent claim than "hard" or "slow," which describe problems complexity theory studies next.
Q3 If the halting problem is undecidable, how do real compilers detect some infinite loops?
They use conservative approximations — pattern-matching common mistakes, bounding execution with timeouts, or proving termination only for restricted, well-behaved cases (like simple counted loops). They never solve the fully general problem; they correctly handle a useful subset and stay silent (or simply run the program) on everything else.
Q4 Is every undecidable problem also intractable (slow)?
No — these are orthogonal ideas. Decidability asks "can any algorithm solve this at all?" Complexity, covered next, asks "given that an algorithm exists, how fast can the fastest one possibly be?" A problem can be decidable yet extremely slow to solve, which is exactly the territory of Chapter 19.
How the ideas connect
Every key idea in this chapter, branching from the core concept — use it to see the whole picture at a glance.
The process, step by step
Worked problems, step by step
Follow each solution line by line, then try to reproduce it on paper before moving on.
Example 1A rule says: in state q0 reading 1, write 0, move right, go to q1. The head is on a 1 in q0. What happens?
- Match (q0, 1) in the table.
- Apply the action.
Example 2Why can no program decide, for every program and input, whether it halts?
- Assume such a decider H exists.
- Build a program that halts exactly when H says it loops, a contradiction.
Now you try
Work each one out first, then tap to reveal the worked answer.