Imagine you were tasked with the mission of finding out which things can and cannot be calculated with a computer, given that you had infinite memory and infinite time.
Given the generality of computers, you might be tempted to say everything, but it turns out that this is not the case.
To begin tackling this problem, we need a formal notion of what it means to compute something. We are going to focus on computing natural functions; that is, functions which transform natural numbers to natural numbers.
As natural numbers can be used to encode pretty much anything, this class of computations is far wider than one might initially think. For example, we canfinite sequences of arbitrary length of natural numbers in a single natural number, and we can encode words in sequences of numbers.
We are going to say that a computer program computes a certain natural function if on input \(n\) it returns the result of applying such function to \(n\), for every natural \(n\). We note that programs do not have to halt on inputs; they may just run forever. In that case, we will say that the program is computing a partial function, which is not defined on some inputs.
So computers cannot compute everything! <div>
The diagonal function
We are going to construct a concrete example of a function which is not computable.
Imagine an infinite list of every possible Python program, associated with the infinite outputs each would produce if we feed it the sequence of natural numbers \(1,2,3,...\).
Thus, we may end up with a table such as this:
program #1: 0->1, 1->X, 2->3,…
Where an X means that the program does not halt on that input or does not return an output, and thus the function it represents is not defined there.
Now, we are going to construct an explicit function which is not computable using this table.
Let \(DIAG\) be such that:
The notation \(M_n\) means “the \(n\)th program in our enumeration of programs”. Let’s see what we have done there. We are looking at the \(n\)th entry of every program and saying that this function on input \(n\) outputs something different that the \(n\)th program. This technique is called , and it is ubiquitous in computability and logic.
The function \(DIAG\), known as the , is guaranteed to disagree with every program on at least one input. Thus, there cannot be a program which computes \(DIAG\)!
The halting function
The reader may at this point be however not satisfied with such an unnatural example of an uncomputable function. After all, who is going to want to compute such a weird thing? Do not dismay, for there is a much better example: the halting function.
Let \(HALT\) be defined as:
That is, the function \(HALT\) decides whether a given program is going to halt on a particular input. Now that is interesting.
To cite one imaginative use of the halting function, one could for example code a program which on any input simply ignores the input and starts looking for a counterexample of the Collatz conjecture, halting iff it finds one. Then we could use the halting function to see whether the conjecture is true or false.
Sadly, \(HALT\) is not computable. We are going to give two proofs of this fact, one by reduction to the diagonal function and other by diagonalization.
Proof by reduction
Proofs by reduction are quite common in computability. We start by supposing that the function we are dealing with, in this case \(HALT\), is computable, and then we proceed to use this assumption to show that if that was the case we could compute another function we know is uncomputable.
Suppose we have a program, which we are going to represent by \(HALT\), which computes the halting function.
Then we could write a program such as this one:
DIAGONAL(n): if HALT(n,n): return 1 if M_n==0 else return 0 else return 0
Which computes the diagonal function. As we know such a program cannot possibly exist, we conclude by reductio ad absurdum that \(HALT\) is not computable.
Prove that \(HALT\) is reducible to \(DIAGONAL\); ie, that if \(DIAGONAL\) was computable we could compute \(HALT\).
AUX(x): PROG(n) return 0
That is, \(AUX\) is the program which on any input first runs \(PROG\) on input \(n\) and then outputs \(0\) if \(PROG\) ever halts.
Then \(DIAG(\ulcorner AUX\urcorner)==1\) iff \(PROG\) halts on input \(n\). <div><div>
Proof by diagonalization
Another nifty proof of uncomputability comes from diagonalization.
As before, let’s suppose \(HALT\) is computable. Then we could write a program such as this one:
prog(n): if HALT(n,n)==1: loop forever else return 0
The program halts on input \(n\) iff \(M_n\) does not halt on input \(n\).
Now, let \(\ulcorner prog\urcorner\) represent the number in the list of the program which occupies the \(prog\) program. Here comes the question: does \(prog\) halt when its input is \(\ulcorner prog \urcorner\)?
Supposing either yes or no leads to a contradiction, so we conclude that such a program cannot possibly exist, and it follows from the contradiction that \(HALT\) is not computable.
So now you are ready to start your journey through the realm of computability!
Suggested next steps include examining other uncomputable functions such as the, or proceeding to .
Mathematics is the study of numbers and other ideal objects that can be described by axioms.