A Turing machine has a semi-infinite tape. Each cell in the tape contains a single symbol from the tape alphabet. Empty cells contain space characters.
The Turing machine has a tape head that can view one cell at a time.
In addition to its tape, a Turing machine has a control made up of states and transitions.
Each transition has three parts: a read, a write, and a move. The read specifies what the tape head should look for in the current cell. If the current cell contains the character listed, the machine can make the transition. When the machine makes the transition it replaces the symbol read with the new symbol, and then moves the tape head one space in the direction indicated.
A transition can leave a cell unaltered by simply reading and writing the same symbol.
This type of transition occurs quite often, so the author uses a more convenient alternative notation for transitions that leave a cell unchanged:
A widely used strategy in Turing machine programs is to replace symbols on the tape with alternative symbols. This strategy is used to mark special locations on the tape. The most common example of this is marking the start of the tape.
This facilitates rewinding the tape head to the start of the tape. The machine simply moves left past regular symbols until it encounters one of the special marking symbols:
Attempting to move the tape head left past the start of the tape results in the tape head staying where it is. Thus state 4 will have the tape head positioned over the start of the tape.
For our first example we will consider a simple language whose strings use the input alphabet Σ = {0,1}. Our first example language is
L1 = { w | w has even length }
Here is the first draft for the control for the machine that recognizes this language.
Clearly, here state 1 is the state the machine will be in after having seen an even number of characters and state 2 is the state the machine will be in after having seen an odd number of characters.
The machine is not yet complete, because we haven't added logic to handle the case where the tape head has run past the end of the input, and we also don't have a way for the machine to accept its input.
Turing machines have final states, just as DFAs and PDAs do. Unlike those earlier machines, Turing machines have both accepting final states and rejecting final states. When a Turing machine enters a final state it will stop. If the final state it entered is an accepting state, the machine will accept its original input. If the final state is a rejecting state, the machine rejects its input.
Once we have discovered how to solve a problem via a Turing machine program, we will often find that we can reuse that Turing machine to do the same task in the context of a larger Turing machine program.
Here is a typical example. Suppose we wanted to make a Turing machine to recognize the language
L2 = { ww | w ∈ Σ* }
A useful first step is to confirm that the length of the input string is even and has length at least two. If we determine it is odd we can immediately stop and rejcct. Here is a Turing machine that can scan its input to determine whether or not the input is of even length and reject any odd length input. To facilitate rewinding to the start in later stages of the program, we use the common strategy of replacing the first character in the input with a marking character.
We can now use this initial machine as a first stage in a larger machine. Instead of ending with the accept state, the machine goes on to the next phase. After completing the first stage, we know that the tape head is over the rightmost character in the input.
In the next phase we make a series of passes across the tape. In each pass we replace a single character on the left side and a single character on the right side. On the left we replaces 0s with cs, 1s with ds. On the right we replace 0s with es and 1s with fs. After each left to right pass we rewind back until we encounter an a, b, c, or d. When we reach the point where the es and fs meet up with the cs and ds we know that we have replaced all of the 0s and 1s in the original, and can then rewind to the start. Here is what the second stage in the machine looks like.
Here I have introduced some condensed notation. The notation (0|1)→(e|f),L means "If you see a 0 replace it with an e and move left. If you see a 1 replace it with an f and move left."
After the second stage all the 0s in the left half of the input have been replaced with cs, and 1s have been replaced with ds. On the right all 0s have been replaced with es and all 1s have been replaced with fs.
The final stage matches characters on the left with characters on the right. Once again, we do this as a series of passes. On each pass we look for a c or d on the left, and move right until will find a matching e or f. Each time we find a matching pair we replace both matching characters with an x to indicate that we done with those characters.
Turing machines are sufficiently complex that there are ample opportunities make mistakes when setting up a machine. To increase our confidence that the machine we have built is correct, we should do some runs through the machine. Below you will find a complete example of a run.
In this example we will run the machine on the input s = 110110. This is an input the machine should accept.
Here is the start configuration of the machine. We start with the input on the tape, the tape head over the first symbol in the input, and the control at state 1. (This sequence of steps is best viewed as a slide show. To start the slide show select Start Slide Show from the Slides menu. To advance from one slide to the next, press the right arrow key. To exit the slide show press the escape key.)
If the exercise of constructing the Turing machine for the example above taught you anything, it may be that building one of these machines is a tedious and error prone process. The one bit of good news from the last example is that almost all Turing machines can be decomposed into distinct stages. If we have a clear idea of what each stage has to accomplish, this makes our work easier.
An alternative to building the full machine in all its detail is to instead identify the stages we need and write out a careful description in words for what each stage is meant to do. This can act as a road map for building the stages.
For example, the machine above can be described in this way:
On input x:
In this initial description I have only written down what happens when everything goes right. This is the typical sequence when we input an x that is in the language. To complete our description, we have to anticipate everything that can go wrong. A good way to handle these cases is by adding subpoints to our main points that describe what can go wrong at various stages. Here is the more complete description with the error cases added:
On input x:
Going forward, we will very rarely be constructing full Turing machines. In almost every case we will settle for a high level description in words that breaks the machine into stages and describes carefully what each stage does. If the descriptions are clear enough and simple enough, we could always go back later and build the submachines for each of the stages if we had to.
In many of the machines we build we will need to do multiple passes across the tape. An annoying technical issue that comes up in this context is rewinding the tape head to the start of the tape. This is difficult to do, since it may be hard to tell that we have reached the left end of the tape. We can mark the first first symbol in the tape in some special way, but this often introduces additional complexity to the machine.
An alternative strategy is to start by running a subroutine that moves every character on the tape one space to the right and places a space at the start of the tape. After doing this, rewinding to the start of the tape is easy, since we just have to move left until we see the space.
Here is a fragment of a Turing machine that moves every symbol on the tape one space to the right and places a blank in the first cell. After moving everything, the fragment rewinds the tape head so it ends up over the first nonblank character in the tape.
The loop on state 4 demonstrates how to rewind to the start of the tape.