“I was peeling a red apple from the garden when I suddenly understood that life would only ever give me a series of wonderfully insoluble problems. With that thought, an ocean of profound peace entered my heart.”
—Christian Bobin
Given an input string, a Turing Machine can accept, reject, or run forever without providing an answer. This third possibililty means we have to be more careful about our definitions.
A Turing Machine accepts a string if halts-and-accepts when started on a tape containing the string .
A Turing Machine rejects a string if halts-and-rejects when started on a tape containing the string .
The language of a Turing Machine , denoted , is the set of strings that accepts.
So if a string is not in , there are be two potential reasons: either eventually rejects , or runs forever without specifically accepting or rejecting .
A language is decidable if it is the language of some terminating TM.
So the following are all equivalent ways of saying that a language is decidable:
A language is undecidable if it is not decidable.
The family of all decidable languages is closed under union, intersection, and complement. We prove the last of these:
If is a decidable language, then is decidable.
Proof:
Assume is a decidable language.
Then there is a TM which decides (i.e., accepts every string in and rejects every string not in ).
Let be a copy of except we swap transitions to the accept and reject states.
Then accepts every string not in (i.e., every string in ) and rejects every string in (i.e., every string not in ).
Thus decides , and is decidable.
We now turn to our first concrete examples of an undecidable language, . We start with some simpler (decidable) languages for contrast.
The notation means ” encoded as a string.”
For example, if is a Turing Machine then is the full specification of as a finite string.
Similarly if is a DFA and is a string in the alphabet of that DFA, then is a string that specifies both a description of (its states, transitions, etc.) and a the contents of the string .
The following languages are decidable:
Proof:
However, when we move from DFAs and CFGs to Turing Machines, it is undecidable whether a machine will accept a given string.
The language
is undecidable.
Proof (by contradiction):
Suppose were decidable.
Then there is a Turing Machine (a piece of code) decide_Atm
that took as input (a string containing both the
specification/code for a machine and a specific input string for
the machine ) and always told us whether would accept or not.
Note: decide_Atm can’t check this by running on , because that
might never terminate. decide_Atm would presumably study the
definition/code of and the input string and do some arbitrarily
complex calculations to predict the behavior of on that input.
We could then construct a “bigger” Turing Machine C to perform
a calculation according to the following pseudocode:
def Cantor(x):
return not (decide_Atm(x, x))The idea is that we would run Cantor on the description
of some a Turing Machine; Cantor would use the
procedure decide_Atm to ask whether Turing Machine would accept
the string or not (i.e., whether ’s source code
is a string that machine would accept). Whatever that answer is, C
then returns the opposite.
Since we have assumed decide_Atm can be implemented and always returns
an answer, we can also implement C and be sure it always returns an
answer as well.
For example, if was a Turing Machine that checked whether its input was an even-length palindrome, then would probably return True (accept):
decide_Atm() would predict
that and return False (reject).Cantor negates this, and returns True (accepts).So far, so good. The problem is this: we’ve implemented Cantor
as a piece of code (a Turing Machine). What happens if instead of
applying Cantor to the code for a palindrome-checker, we apply
Cantor to its own source code? According to the definition
def Cantor(x):
return not (decide_Atm(x, x))the call Cantor() would ask
decide_ATM whether the machine Cantor accepts when given its own
source code as input, and then returns the opposite answer. This is a
problem, because
Cantor can’t accept its own source code. If it did, decide_Atm
would predict this fact. Then Cantor would negate this
answer and return False (rejecting its own source code).Cantor can’t reject its own source code. If it did, decide_Atm
would predict that fact. Then Cantor would negate this
answer and return True (accepting its own source code).Cantor can’t compute forever; it just calls decide_Atm
(which always returns an answer), and takes one more step to negate
that answer.We have reached a contradition:
Cantor() cannot accept,
cannot reject, and cannot run forever.
Therefore, our assumption (that we could implement a decider for ) must be wrong, and is not decidable.
One might worry that there’s something fishy about applying a program to its own source code, but this happens all the time in the real world, e.g.,
wc can be used to count the number of lines in a
text file. This program is implemented in C; if we wanted to whether
this code is long and complicated, it would be perfectly reasonably
to run wc on its own code w.c to see how many lines are in the
source code.clang++ C++ compiler is written in C++. To add a new feature
to the compiler (e.g., when a new standard for C++ adds extra
features to the C++ languages), we edit the C++ source, compile that
C++ source with the old clang++ compiler, and produce a new
clang++ executable having the new feature.So the idea of applying a program to its own source code is not inherently contradictory.
Saying that is undecidable means that there is no Turing Machine (and hence no code and no algorithm) that can take an arbitrary TM and an arbitrary input, and correctly predict (in finite time) whether this TM will accept that input.
It does not mean we can never answer the question! It’s quite easy to build a TM that never accepts. If we ask whether this particular machine accepts , we have no problem deciding the answer is “no.”
Conversely, we might be able to look at a more interesting Turing Machine and realize it accepts all (and only) even-length palindromes.
But there is no algorithm for deciding acceptance that works in all cases.