The first step in studying the sorts of possible computations (and more interestingly, those things which cannot be computed) is to define exactly what we mean by a “computation.” At a high level, this is easy: a computation is simply a function. Given some input, produce the appropriate output.
Unfortunately this is much too general. For instance, we could define almost anything we want in terms of functions. Let
Because our inputs may be long and complicated, our model should break an input into pieces and work on the pieces in sequence. This motivates the following two definitions:
Definition: An alphabet is a finite set, usually denoted
Definition:
We will henceforth let inputs to our model be elements of
Since
Now we need a clear objective for our model, and we will phrase it again in terms of
Notice that this is a bit different from our colloquial idea of “computation.” Specifically, all our model will do is determine presence of an element in a set which has a potentially complicated construction. While it at first seems that this does not include things like “adding two numbers,” it turns out that we may phrase such objectives in terms of acceptance and rejection. Let
Colloquially, we have defined “computation” as recognizing all inputs which satisfy a qestion. Moreover, we will see that very many complex problems can be recognized as recognition problems, including things that are logically impossible to compute within our model.
The reason we have been heretofore vague in naming our model is that we will actually define four different models of progressively stronger computational ability, and this framework will hold for all of them. So here our first try.
Deterministic Finite Automata
This definition comes from the intuitive idea that a computation can be carried out via a set of states and transitions between those states. A very simple example is a light switch, which has the states ‘on’ and ‘off’, and which can accept as input ‘switch’ or ‘do nothing’. This model is rigorized here:
Definition: A deterministic finite automaton, abbreviated DFA, is a five-tuple
is a set of states, with initial state . is our working alphabet, and inputs come from . is a transition function , which maps the current state and next input character to the next appropriate state. is a set of final states. If the last input character lands us in any state , we accept.
Rigorously, let our input word
Of course, we have some examples of DFAs. The simplest possible DFA has only one state,

Indeed, this visualization provides a convenient way for us to write out the transition function. We simply draw an edge originating at each state for each element of our alphabet. When two or more inputs have the same behavior, we label one edge with multiple input symbols, as above.
Here is a more interesting example: let

The shaded state,
Let
Indeed, it has been proven that no such DFA exists. The key to the proof lies in an esoteric lemma called the Pumping Lemma. Being a notational beast of a lemma, we will not state it rigorously. Colloquially, the lemma says that if
Before we increase our model’s expressive power, let us make an “enhancement” to the DFA.
Nondeterministic Finite Automata
Following the colloquial definition of nondeterminism, we can design our computational system to make state transitions “branch out.” This will be made clear by a slight modification of the DFA.
Definition: A nondeterministic finite automaton, abbreviated NFA, is a DFA with the following two modifications:
- Instead of having a single state
at each step, we allow a set of possible states, which we denote . - Our initial state
is replaced with an initial set of states . - We include
in , allowing for immediate transitions that require no input. - The transition function
now has signature , where denotes the power set of , the set of all subsets of . - At each step (with input character
), we map over to get , i.e. - The NFA accepts if any state in
is final.
In this way, our machine can get a character as input, and proceed to be in two separate states at the same time. If any of the branches of computation accept, then they all accept.
Extending our example from DFAs, here is an NFA which accepts the language of binary strings which have either an even number of zeros or an even number of ones (or both).

Here,
Now, with the added strength of nondeterminism, we might expect that NFAs can compute some things that DFAs cannot. Amazingly, this is not the case. We point the reader to a more detailed description, but trust that our quick explanation will give the necessary intuition to see the equivalence.
The key observation is that despite nondeterminism, there is still a finite number of possible states an NFA can be in. Specifically, there are at most
In other words, every NFA has a corresponding DFA which recognizes precisely the same language. On the other hand, every DFA is trivially an NFA. Thus, the class of languages which NFAs recognize is also the regular languages.
So we have discovered that regular languages, and hence the DFAs and NFAs which recognize them, are quite limited in expressive power. Now let’s truly beef up our model with some mathematical steroids.
Pushdown Automata
Okay, so these steroids won’t be that strong. Ideally, we just want to add as little as possible to our model and still make it more expressive. This way we can better understand the nuances of computational power, thus strengthening our intuition. While “little” is a subjective notion, lots of “littler” things were tried before arriving at a model that was not equivalent in power to a DFA. We will refrain from explaining them here, except to say that DFAs are equivalent in power to the class of formal Regular Expressions (hence the name, regular languages), which most programmers are familiar with on an informal level.
Definition: A pushdown automaton, denoted PDA, is an NFA with two additional components:
Here, the left hand
For brevity of a description of
Recalling our discussion of computational ability in our post on Conway’s Game of Life, we recognize that a PDA’s new power is in its ability to grow without bound. Specifically, the stack has no limit to its size, and we can remember important pieces of input which would otherwise be discarded.
This modification seems like it was made just for the

Here
Neato! It seems like we’ve got a huge boost in computational power now that we can store some information. Wrong. As great as a stack is, it’s not good enough. Here’s a language that a PDA cannot solve:
Time to up the steroid dosage. Surprisingly enough, it turns out that our solution will be equivalent to adding another stack! In doing so, we will achieve a level of computational expression so powerful that nothing we know of today can surpass it. We call this panacea of computational woes a Turing machine, and we will cover both it and the wild problems it cannot compute next time.
Until then!
Want to respond? Send me an email, post a webmention, or find me elsewhere on the internet.