Exercises — Halting problem — undecidability proof by diagonalization
Everywhere below, means "the source code of program written as a string," and means "the code of paired with an input ." We can feed into a program because code is just data — a string like any other.
Level 1 — Recognition
Goal: can you state the definitions and read the machinery correctly?
Exercise 1.1
Write, in set-builder form, the exact language the Halting Problem asks us to decide. Then say in one word whether it is decidable.
Recall Solution 1.1
Undecidable. No total program (one that always halts with a yes/no answer) decides membership in for every pair.
Exercise 1.2
The parent note defines the troublemaker D. Fill the two blanks:
D(P):
if H(P, P) == HALT:
__________ # blank 1
else:
__________ # blank 2
Recall Solution 1.2
- Blank 1:
loop forever - Blank 2:
halt
D always does the opposite of H's prediction about P running on its own code.
Exercise 1.3
Match each term to its meaning:
- decidable 2. recognizable 3. semi-decider
- (a) a program that halts-and-says-yes on members, but may run forever on non-members
- (b) a total program that always halts with the correct yes/no
- (c) the class of languages some program accepts (same as recognizable)
Recall Solution 1.3
1 → (b), 2 → (c), 3 → (a). See Decidable vs Recognizable Languages: every decidable language is recognizable, but is recognizable and not decidable.
Level 2 — Application
Goal: run the machinery on concrete inputs.
Exercise 2.1
Assume H exists and is correct. Trace D(D) under the assumption "D(D) halts." Which branch of D must have executed, and what did H(D,D) return? Where is the contradiction?
Recall Solution 2.1
If D(D) halts, the only branch that halts is the else. The else runs exactly when H(D,D) == LOOP. So H claimed D(D) runs forever — but we assumed it halts. H is wrong, contradicting "H correct." ∎
Exercise 2.2
Now trace the other case: assume "D(D) loops forever." Repeat the reasoning.
Recall Solution 2.2
If D(D) loops, the if-branch was taken (that is the only branch that loops). The if-branch runs exactly when H(D,D) == HALT. So H claimed D(D) halts — but we assumed it loops. H is wrong again. Both cases fail, so the equivalence
holds, and a statement equal to its own negation is impossible.
Exercise 2.3
Consider these three tiny programs. For each, say what a correct H would answer — and note this is possible for specific cases even though is undecidable overall.
A(w): print("hi")
B(w): while True: pass
C(w): n = len(w)
while n > 0: n = n - 1
Recall Solution 2.3
A: halts — it prints once and stops (any input).B: loops forever — the emptywhile Truenever exits.C: halts —nstarts finite and strictly decreases to , so the loop runs exactly times.
Deciding specific programs is easy. Undecidability only forbids one algorithm that is correct for all programs at once.
Level 3 — Analysis
Goal: see WHY each part of the argument is load-bearing.
Exercise 3.1 (the diagonal table)
Look at Figure 1. Rows are programs ; columns are the same programs used as inputs. Cell is H if halts on , else L. D's row is defined to disagree with cell . Explain, using column , why D cannot equal any in the list.

Recall Solution 3.1
D's behaviour on input is defined to be the flip of the diagonal cell — i.e. the flip of "how behaves on itself." So D and differ in column . This holds for every : D differs from in column 1, from in column 2, and so on down the amber diagonal.
A program that differs from every row cannot be any row — yet the list was supposed to contain all programs, including D. Contradiction. This is exactly Cantor's Diagonal Argument applied to the program×program table.
Exercise 3.2
Why must H be total (always halt) for the proof to work? What breaks if H is only a semi-decider?
Recall Solution 3.2
The proof needs H(D,D) to return a value so that D can then act on it. If H were allowed to loop forever on some inputs, then on the fatal input H(D,D) might simply never return — and D would just hang inside its if test. No answer, no contradiction. The paradox is triggered precisely by H being forced to always give a definite HALT/LOOP. That is why we assume H is a total decider, not a mere recognizer.
Exercise 3.3
The parent note says " is recognizable." Sketch the recognizer and explain which half of the yes/no answer it can and cannot deliver.
Recall Solution 3.3
Recognizer R(⟨P,w⟩): run a Universal Turing Machine to simulate on step by step; if the simulation halts, output yes.
- On a yes-instance ( halts on ): the simulation eventually stops →
Routputs yes. ✔ - On a no-instance ( loops): the simulation runs forever →
Rnever outputs anything. ✘
So R confirms membership but can never confirm non-membership. That one-sided behaviour is the definition of a recognizer that is not a decider.
Level 4 — Synthesis
Goal: build new undecidability results using reductions.
Exercise 4.1 (empty-input halting)
Prove is undecidable, assuming is.
Recall Solution 4.1
Reduction (see Reductions and Mapping Reducibility). Given any , construct a new program:
P_w(x): # ignores its input x
run P on the hardcoded string w
Then halts on empty input halts on . If a decider for existed, we could decide by building and running . But is undecidable — contradiction. Hence is undecidable. ∎
Exercise 4.2 (accepts-a-specific-string)
Let . Show it is undecidable by reducing from .
Recall Solution 4.2
Given , build:
Q(x):
run P on empty input # first, do P's empty-input computation
accept # only reached if that computation halted
accepts every string (in particular 01) halts on empty input; if loops on empty input, never accepts anything, so it does not accept 01. Thus . A decider for would decide — impossible. So is undecidable. ∎ (This is a hand-built instance of what Rice's Theorem proves in general.)
Exercise 4.3 (why the reduction must go the right way)
A student "proves" decidable by reducing to a decidable language . State the flaw.
Recall Solution 4.3
A reduction transfers decidability from to (if decidable then decidable), and undecidability from to . To conclude undecidable you reduce a known-undecidable into (i.e. with ). Reducing into a decidable is impossible in the first place — you can never actually build such a mapping, because it would make decidable, which it isn't. The student is assuming the very thing that cannot exist. Direction is everything.
Level 5 — Mastery
Goal: invent a fresh diagonal argument and defend it.
Exercise 5.1 (diagonalize a new claimed decider)
Suppose someone claims a total decider T(⟨P⟩) that outputs YES iff halts on its own code (the self-halting question). Construct a diagonal program that refutes T, and trace the contradiction.
Recall Solution 5.1
Build:
G(): # G takes no useful input; call G on ⟨G⟩
if T(⟨G⟩) == YES: # T predicts G halts on its own code
loop forever
else:
halt
Run G on :
- If
Ghalts on its own code → theelseran →T(⟨G⟩)=NO(T said it doesn't self-halt) — but it did. Wrong. - If
Gloops on its own code → theifran →T(⟨G⟩)=YES(T said it self-halts) — but it looped. Wrong.
Either way T is wrong on the single input , so no correct total T exists. The self-halting language is undecidable — a direct diagonal, no reduction needed.
Exercise 5.2 (spot the illegal step)
Below is a "proof" that is decidable. Exactly one line is illegal. Identify it and explain.
1. Let M be the machine that on ⟨P,w⟩ simulates P on w.
2. If the simulation halts within k steps, output YES.
3. Choose k large enough that every halting computation finishes. # <-- ?
4. If not halted by step k, output NO.
Recall Solution 5.2
Line 3 is illegal. There is no single finite that bounds every halting computation: halting programs can run arbitrarily long (a program can halt after steps, steps, or after Ackermann-many steps). Picking a universal cutoff would itself require knowing the running times in advance — i.e. deciding halting. Line 3 smuggles in the answer it claims to compute. Everything else is fine; the whole "proof" collapses on that one assumption.
Exercise 5.3 (connect to the foundations)
In one or two sentences each, explain how the halting result relates to: (a) the Church–Turing Thesis, and (b) Rice's Theorem.
Recall Solution 5.3
(a) Church–Turing Thesis: it asserts that any effective/mechanical procedure is captured by a Turing machine. Because of it, "no Turing machine decides " upgrades to "no algorithm whatsoever — in any language, on any hardware — decides halting." The impossibility is absolute, not Turing-specific.
(b) Rice's Theorem: it generalizes the halting result — every non-trivial semantic property of a program's behaviour (does it halt? accept 01? compute a constant?) is undecidable. Halting is the flagship special case; Exercises 4.1–4.2 are hand-built instances of what Rice proves wholesale.
Recall One-line self-test before you leave
The engine of all of this: halts does not halt. If you can rebuild that line and the two-case trace from memory, you own the proof.