Chapter 18

Computability & Turing Machines

College
At a glance
Core ideaA simple tape-and-head machine can compute anything computable.
Key termHalting problem — undecidable: no program decides it in general.
You can…Simulate a Turing machine and follow its transition table.
Watch out"Undecidable" means provably impossible, not merely unsolved yet.
Theory

In 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.

Explanation

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.

Practical

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.

Q&A
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.

Concept mind map

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.

tapeheadstatetransition tablehalting problemundecidableTuring machines
Infographic

The process, step by step

Step 1ReadThe head reads the symbol under it on the tape.
Step 2Look up ruleThe transition table maps (state, symbol) to an action.
Step 3Write and moveWrite a symbol, move the head left or right.
Step 4Change stateEnter the next state; halt if it is an accepting or final state.
Solved examples

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?

  1. Match (q0, 1) in the table.
  2. Apply the action.

Example 2Why can no program decide, for every program and input, whether it halts?

  1. Assume such a decider H exists.
  2. Build a program that halts exactly when H says it loops, a contradiction.
Practice problem set

Now you try

Work each one out first, then tap to reveal the worked answer.

1What are the parts of a Turing machine?
An infinite tape of cells, a read/write head, a current state, and a transition table of rules.
2What does the transition table do?
For each (state, symbol) pair it says what to write, which way to move, and the next state.
3What is the halting problem?
The question of whether a given program halts on a given input; it is undecidable in general.
4What does undecidable mean?
No algorithm can give the correct yes/no answer for every possible input.
5Why do we study such a simple machine?
Despite its simplicity it can compute anything computable, defining the limits of computation.
6What is the Church-Turing thesis?
The claim that any effectively computable function can be computed by a Turing machine.