Theory of computation

The theory of computing is the science of computation, fundamental for the computer scientist; What can we really compute mechanically; How fast can we do it, and how much memory is required. There are many applications that emerge from this subject, including all of complier design and the theory of writing compilers and writing programs to translate languages; All of which emerge from the theory of computation.

Some of the models of computation that will be discussed will, as a side effect, help us to write compilers. When a computer is modelled, the architecture of the CPU is modelled with a finite state machine. All string searching in a text editor, also uses a finite state machine.

When you do any compiling or design a language such as XML, you describe it with a grammar, that grammar is the next level of model of computation. We will be discussing exactly that, the different models of computation that can be used to compute.

The things that we will be considering to compute will only require a boolean yes or no response from the machine and as such all those things that we will analyse can be thought of in terms of sets. We will define alphabets, of differing types and from those alphabets we will construct strings to compute, to search along or iterate over.

Finite State Machines

Hierarchy

A bullseye map of the different heirarchy of state machines.
Fig 01.01

Languages can be defined in three ways, by a machine by a grammar or by an expression.

Ask yourself, "can I write a program to solve this problem using a finite amount of memory", this will tell you whether or not you can use a finite state machine to solve a problem.

Every finite state machine works upon a predefined alphabet, the most simple of which is binary.

How to define a FSM

Adding the arrows.

There is always one arrow pointing from nothing to one of the states; This arrow defines that state as the start.

The final state.

The final state must also be defined, this can be done with a double circle.

An even number of 0's

simple finite state machine
Fig 01.02

An even number of 1's and even number of 0's

First define the set of states, all possible combination of 0's and 1's in which the number of both are even. As we can see when considering two bits with discrete math, there are a total four possible states. We can represent these states with the letters EE for even 1 even 0, EO, OE, and OO consecutively for even 0's odd 1's, odd 1's even 0's and then finally odd 0's odd 1's. More clearly depicted by this state machine diagram.

finite state machine, four states
Fig 01.03

Binary numbers that are divisible by four.

To check whether a binary number is divisible by four we need to check that the last two bits of the string are 0. To first demonstrate a simple case, it is easier to write a machine that checks only the first two bits. Notice the state D denoting a dead machine that has failed to complete and has halted.

This machine finds strings that start with two 0's, which is not what we want, but it gives us an idea of what we need to do.

finite state machine, four states
Fig 01.04

To simplify things we have made a machine that signals the solved state every time that two consecutive 0's are encountered; However we are not starting in the right place in order to find those bits at the end of a string; For this machine to work, we will need to start at the final state. So how do we make a machine that will find strings that end with two 0's?

finite state machine attempting to detect a string that begins
							 with two 0 bits.
Fig 01.05

By moving the start arrow to the end of the chain, we insure that the 0 bits are sought last and not at the beginning. A few further modifications are required so that the machine will function as required; We no longer accept empty strings, nor single 0's

finite state machine similar to the previous drawing with its
							 starting arrow mooved to the last state, now detecting 0 bits at
							 the end of its input string
Fig 01.06

The trick here in solving was to reverse the process of the machine so as to find a simple way to deal with the problem, we should always consider that any solvable finite state machine has an inverse of itself problematically; These reflections are known as closure machines, or compliments.

Closures, compliments, closed under compliment

Any set that has a finite state machine that accepts it, is known as a regular set. Regular sets are closed under certain kinds of operations; Which means that if we perform the operation upon the set, giving a new set, for example were we to compliment, from a set we calculate everything that is not in that set, we have performed an operation upon that set to derive its compliment. If that new set is always also a regular set, without any exceptions, then we say that the collection of regular sets are closed under compliment.

If we were to devise a finite state machine for all of the strings that are not divisible by four, we could simply take our machine and toggle all of its current states. This simple fact is proof that if you are able to demonstrate that a set is finite, thus regular for one problem, you can also show that the same is true for its compliment by simply toggling the state of its nodes.

The proof really is that simple. Take any finite state machine defining a language of strings, for which we wish to find its compliment; All that need be done is to toggle the states; The two sets are said to be, closed under compliment.

Contains 110110

To find a machine that deals with a longer string, first we require a series of states that represents that string. The symbols used in describing the matching of strings is different in different books and schools, some using ε epsilon and others λ lambda, to denote the null or nil value; Otherwise said, no state. The process of building up the machine this way is algorithmic, the algorithm for which is known as the 'Knuth Morris Pratt algorithm'. We will use ε to denote our null state, the state that exists before staring; The reason for this will become clear as we continue on.

It is easy to follow the construction of this machine, observe how each state is described by, and grows in accordance with the denomination of the arrow that precedes it. Conversely, any arrow not having the correct value to continue, leads back to the nearest state that does assume its new structure.

finite state machine showing a serch for a string containing
							 110110.
Fig 01.07

Reversal becomes non deterministic

To find the reverse string of a previously constructed finite state machines target, we invert all of the arrows in its chain of states; Then transform its final state into its start state, and its start state into its final state. In so doing, we soon notice that the newly created finite state machine makes no sense; Our first node has two choices emerging from it, to where there were previously several state nodes converging upon its end. Although this facet of the reversal is not yet of any use to us; We will soon find this to be the beginnings of a non deterministic finite state machine.

This notion can be somewhat peculiar to comprehend, I only urge that you to continue on, should it be that you are finding this difficult to digest; The next few examples will lead up to a demonstration of this principle in practice, that is far simpler than it might first appear.

Every 1 followed by at least two 0's

This machine expands quite naturally from left to right. On arriving at the final state, upon closer inspection it can be seen that the final state is nothing but a repetition of the starting state, due to the recursive nature of the problem at hand.

a finite state machine incorectly depicting an effort to solve
							 for the case in which every 1 in a string is followed by two
							 0's.
Fig 01.08

We should be aware that their may exist a solution that is more efficient than that which first appears, once all duplicate states have been removed.

The above finite state machine has been corrected by removing a
							 state and having the the sucessful move directed back to the
							 start of the machine.
Fig 01.09

Not divisible by 3

For this problem it is not so simple as looking at the state of the last bit, as was the case for division by two; Division as a computation requires space. The space required grows with the complexity of the solution in a manor that is proportional to the log of the length of the string that is under examination. Without the use of memory we cannot achieve this feat. However, all that we require to solve our current problem is the remainder and not the full solution of a division; Finding the remainder is achievable with a finite state machine.

To calculate the remainder we will require three states; Remainder of one, two and zero or no remainder; The modulo results of the remainder of this division, will be the three states of of our machine.

For the string 1 the remainder would be 1 (1%3 = 1), if we concatenate a 0 to the string to get 10, we need add no remainder for the 0 itself but we will have to double the current value due to the effect of the binary left shift operation, giving us 2; As such the remainder is now also 2 (2%3 = 2) and so on until we have covered the length of our string.

10111 is 23 in decimal and 23%3 is 2, continuing on through our machines calculation we can see that the same result is arrived at, as the next three letters of our sting are 1's and the string is simply looping over the 1 in the 3rd state, the value of which is 2.

A finite state machine that shows the process of testing whether
							 or not the base 10 represntation of a binary string is divisible
							 by 3.
Fig 01.10

Considering the above machine, the following issue arises: If we try to implement its reverse, a machine that seeks strings divisible by three. Which would be our start state; We have two that were the passing cases!

To reverse this particular machine requires a new blank starting state with two empty [ε|λ|e] transitions; Rendering our new machine non deterministic.

A finite state mchine depicting the preveious machine reversed
								 with a correct start for the two possible starting points.
Fig 01.11

A finite state machine is limited, if it were not we would not require unbound memory within a computer and we would have no need of Turing machines. The simplest thing that cannot be achieved with a finite state machine is to count, it is not possible to count to an arbitrary number; Bound by a finite condition, we can only count as high as is the length of our machines state sub string.

Non deterministic finite state machines

Non deterministic state machines do not give us any more power in knowing which finite state machines we can compute, but they do give us the ability to define machines more easily; Improving our ability to recognise which are finite and which are not. For those machines that we are able to describe, having this greater level of abstraction makes this operation far easier to achieve.

When considering a non deterministic finite state machine, we will no longer reject a string if it fails to pass the machine once, due to the existence of choices in states, we must now consider every possible path through the machine, then only do we reject it, if none terminate with the machine in a completion state.


Returning to the previous example 110110 when seeking a string that contains this as a sub string anywhere within its length, that there be two choices for the initial 1 within the strings form, requires that we try every possible case for the machine as it passes through the equivalent state. Take the string 1111011000, the machine must be tried for every instance of 1 whilst in the initial ε state. To make this more compact are readable, we leave some state changes out of the diagram drawing a self referential loop instead.

A non determinate state machine that is a slightly modified
							 version of figure 07.
Fig 01.12

Consider the inverse of this non deterministic machine, the nature of the initial condition will stop any such machine from functioning. Non deterministic machines cannot be inverted to get their compliment, not so easily as deterministic machines. In order to get the compliment, we must first convert it into a deterministic machine and then compliment it; By definition non deterministic state machines are asymmetric.

The main difference then between the finite state machine and the non deterministic finite state machine is one of procedure, the finite state machine can be thought of as a single process where as a non deterministic finite state machine is parallel.

Some refer to this parallelism as guessing, rather than suggesting the machine trying every single path; If a correct path leading to the final state exists, it is simply stated that we have guessed the correct one.

Converting non deterministic machines

By making a non deterministic machine that will end on two zeros, and converting that machine in a systematic way, we can show that any non deterministic finite state machine may be turned into a deterministic finite state machine. We can see that all non deterministic state machines may be converted into deterministic machines using the same method, and as such we can conclude that although non determinism affords us convenience, it brings no extra power to the machine.

The first state of this machine iterates trying every single possible way of running, until it arrives at the case which has the required ending.

A non determinate state machine looks as though it is guessing
							 the correct path.
Fig 01.13

This machine will only accept a string that ends with 00 and nothing else, it is easy to mistakenly leave extra cases, effectively permitting the machine to end in other ways; we must be very careful that this does not happen. It is implicitly assumed that all strings which still have remaining letters upon reaching the end, will go to an unwritten dead state and fail.

For a deterministic machine it would be required of us to draw a loop at the end, with an arrow of 0's returning back to the same state, we could have drawn this here and the non deterministic machine would still be correct, however this is not required, the correct path will eventually be taken, the goal here is the simplicity of the description.

Our next step is to transform this machine into a deterministic finite state machine using a process that is very logical.

The transformation

The first thing that to do when transforming a non deterministic machine into a deterministic machine is to label the states.

A non deterministic state machine with all of its states
							 labled.
Fig 01.14

The next principle is fundamental and has applications within the entire hierarchy of finite state machines, as such we should take care to insure that we have fully understood it.

Once each of the states have been named, iterate over the machine states taking note of all of the possible paths that the machine could take, use this information to draw a new deterministic version of the machine, reflecting all of the possibilities of the original machine, adding states if required, arrows that lead back to prior states should the machine fail, midway if some useful piece of the desired string still remains, else back to the beginning in the case of complete failure.

Our new machine will not display any of the possible dead states of the non deterministic machine, they are not relevant to the deterministic machine either, it only accepts passing strings.

A deterministic state machine that represents the non
							 deterministic machine previously shown in figure 13.
Fig 01.15

The string accepting state of our machine is C and as such it is the final state of our deterministic machine, it is also true that the machine, when in this state, might also be in either of A or B states, but as is the case for the non deterministic machine, all that is required in order to show that the string has passed, is that it be in this state when the string terminates.

We have not incurred any great cost whilst transforming our non deterministic machine into a deterministic one, in fact we have the same number of states after the operation as before starting. The worst case scenario for this procedure is 2^n states where n is the number of states in the original non deterministic machine. So the general trade off when converting from non deterministic to deterministic is a possible exponential growth of the number of states required by the machine.

At the current stage in our abstraction of computation, this is not yet any great cause for concern, the quantity remains finite. However, a little higher up in the abstraction model, we will see that this does become problematic; On the Turing layer, the exponential growth of state translates to an exponential growth in computation time required.