Notes on Models of Computation
H. Conrad Cunningham
014April 2022
Browser Advisory: The HTML version of this textbook requires a browser that supports the display of MathML. A good choice as of April 2022 is a recent version of Firefox from Mozilla.
Why study theory?
In this course, we study the following models:
The mathematical concepts used in the Linz textbook include:
Students in this course should be familiar with the following set concepts from previous study in mathematics:
Laws for operations on the empty set:
DeMorgan’s Laws:
Function means that
Function is a
A relation on and is any subset of .
An equivalence relation is a generalization of equality. It is:
A is a mathematical entity where
is a finite set of vertices (or nodes)
is a finite set of edges (or arcs)
each edge is a pair of vertices
A directed graph (or digraph) is a graph in which each edge has a direction from vertex to vertex .
Edge on a digraph is an outgoing edge from vertex and an incoming edge to vertex .
If there are no directions associated with the edges, then the graph is undirected.
Graphs may be labeled by assigning names or other information to vertices or edges.
We can visualize a graph with a diagram in which the vertices are shown as circles and edges as lines connecting a pair of vertices. For directed graphs, the direction of an edge is shown by an arrow.
Linz Figure 1.1 shows a digraph where and edges .
A sequence of edges is a walk from to . The length of the walk is the total number of edges traversed.
A path is a walk with no edge repeated. A path is simple if no vertex is repeated.
A walk from some vertex to itself is a cycle with base . If no vertex other than the base is repeated, then the cycle is simple.
In Linz Figure 1.1:
If the edges of a graph are labelled, then the label of a walk (or path) is the sequence of edges encountered on a traversal.
A tree is a directed graph with no cycles and a distinct root vertex such that there is exactly one path from the root to every other vertex.
The root of a tree has no incoming edges.
A vertex of a tree without any outgoing edges is a leaf of the tree.
If there is an edge from to in a tree, then:
is the parent of
is a child of
The level associated with each vertex is the number of edges in the path from the root to the vertex.
The height of a tree is the largest level number of any vertex.
If we associated an ordering with the vertices at each level, then the tree is an ordered tree.
The above terminology is illustrated in Linz Figure 1.2.
Students in this course should be familiar with the following proof techniques from previous study in mathematics:
We will see an example of an inductive proof in the next section.
Three fundamental ideas are the major themes of this course:
Our concept of language is an abstraction of the concept of a natural language.
Linz Definition (Alphabet): An alphabet, denoted by , is a finite, nonempty set of symbols.
By convention, we use lowercase letters near the beginning of the English alphabet to represent elements of .
For example, if , then the alphabet has two unique symbols denoted by and .
Linz Definition (String): A string is a finite sequence of symbols from the alphabet.
By convention, we use lowercase letters near the end of the English alphabet to represent strings. We write strings left to right. That is, symbols appearing to the left are before those appearing to the right.
For example, is a string from the above alphabet. The string begins with a and ends with an .
Linz Definition (Concatenation): The concatenation of strings and means appending the symbols of to the right end (i.e., after) the symbols of , denoted by uv.
If and , then .
Definition (Associativity): Operation is associative on set if, for all , , and in , . We often write associative expressions without explicit parentheses, e.g., .
String concatenation is associative, that is, .
Thus we normally just write without parentheses.
Definition (Commutativity): Operation is commutative on set if, for all and in , .
String concatenation is not commutative. That is, .
Linz Definition (Reverse): The reverse of a string , denoted by , is the string with same symbols, but with the order reversed.
If , then .
Linz Definition (Length): The length of a string w, denoted by , is the number of symbols in string .
Linz Definition (Empty String): The empty string, denoted by , is the string with no symbols, that is, .
Definition (Identity Element): An operation has an identity element on set if, for all , .
The empty string is the identity element for concatenation. That is, .
We can define the length of a string with the following inductive definition:
Note: This inductive defintion and proof differs from the textbook. Here we begin with the empty string.
Using the fact that is the identity element and the above definition, we see that
.
Prove .
Noting the definition of length above, we choose to do an induction over string v (or, if you prefer, over the length of , basing induction at 0).
Base case (that is, length is 0)
= | { identity for concatenation } justification for step in braces |
= | { identity for + } |
= | { definition of length } |
Inductive case
(that is, length is greater than 0)
Induction hypothesis:
= | { associativity of concatenation } |
= | { definition of length } |
= | { induction hypothesis } |
= | { associativity of + } |
= | { definition of length (right to left) } |
Thus we have proved . QED.
Linz Definition (Substring): A substring of a string is any string of consecutive symbols in .
If , then the substrings are .
Linz Definition (Prefix, Suffix): If , then is a prefix of and is a suffix.
If , the prefixes are .
Linz Definition (): , for any string w and denotes repetitions of string (or symbol) . We further define .
Linz Definition (Star-Closure): , for alphabet , is the set of all strings obtained by concatenating zero or more symbols from the alphabet.
Note: An alphabet must be a finite set.
Linz Definition (Positive Closure):
Although is finite, and are infinite.
For a string , we also write and to denote zero or more repetitions of the string and one or more repetitions of the string, respectively.
Linz Definition (Language): A language, for some alphabet , is a subset of .
Linz Definition (Sentence): A sentence of some language is any string from (i.e., from ).
Let .
.
is a language on .
Since the language has a finite number of sentences, it is a finite language.
Sentences and are in , but is not.
As with most interesting languages, is an infinite language.
Languages are represented as sets. Operations on languages can be defined in terms of set operations.
Linz Definition (Union, Intersection, and Difference): Language union, intersection, and difference are defined directly as the corresponding operations on sets.
Linz Definition (Concatenation): Language complementation with respect to is defined such that .
Linz Definition (Reverse): Language reverse is defined such that . (That is, reverse all strings.)
Linz Definition (Concatenation): Language concatenation is defined such that .
Linz Definition (): means concatenated with itself times.
and
Definition (Star-Closure): Star-closure (Kleene star) is defined such that .
Definition (Positive Closure): Positive closure is defined such that .
Let .
(where n and m are unrelated).
.
How would we express in and ?
Although set notation is useful, it is not a convenient notation for expressing complicated languages.
Linz Definition 1.1 (Grammar): A grammar is a quadruple where
is a finite set of objects called variables.
is a finite set of objects called terminal symbols.
is a special symbol called the start symbol.
is a finite set of productions.
and are nonempty and disjoint.
Linz Definition (Productions): Productions have form where:
, i.e., is some non-null string of terminals and variables
, i.e., is some, possibly null, string of terminals and variables
Consider application of productions, given :
is applicable to string .
To use the production, substitute for .
Thus the new string is .
We say derives , written .
Continue by applying any applicable productions in arbitrary order.
.
Linz Definition (Derives): means that derives in zero or more production steps.
Linz Definition (Language Generated): Let be a grammar. Then is the language generated by .
That is, is the set of all strings that can be generated from the start symbol using the productions .
Linz Definition (Derivation): A derivation of some sentence is a sequence .
The strings above are sentential forms of the derivation of sentence .
Consider where P is the set of productions
Consider . Hence, .
is a sentence of the language; the other strings in the derivation are sentential forms.
Conjecture: The language formed by this grammar, , is .
Usually, however, it is difficult to construct an explicit set definition for a language generated by a grammar.
Now prove the conjecture.
First, prove that all sentential forms of the language have the structure for by induction on i.
Clearly, is a sentential form, the start symbol.
Assume is a sentential form, show that .
Case 1: If we begin with the assumption and apply production , we get sentential form .
Case 2: If we begin with the assumption and apply production , we get the sentence rather than a sentential form.
Hence, all sentential forms have the form .
Given that is the only production with terminals on the right side, we must apply it to derive any sentence. As we noted in case 2 above, application of the production to any sentential form gives a sentence of the form . QED.
Given .
Recursive production would generate sentential forms .
Need production(s) to add the extra b to the final sentence.
Suggest .
A slightly different grammar might introduce nonterminal A as follows:
To show that a language is generated by a grammar , we must prove:
For every , there is a derivation using .
Every string derived from is in .
Linz Definition (Equivalence): Two grammars are equivalent if they generate the same language.
For example, the two grammars given above for the language are equivalent.
Let and let and denote the number of ’s and ’s in the string .
Let grammar have productions
Let .
Prove .
Informal argument follows. Actual proof would be an induction over length of .
Consider cases for .
Case . Show .
Any production adding an a also adds a b. Thus there is the same number of a’s and b’s.
Case . Show .
Consider or for some .
String was generated by either or in the first step.
Thus .
Consider (or ) for some .
Examine the symbols of w from the left – add 1 for each a, subtract 1 for each b.
Since sum must be 0 at right, there must be a point where the sum crosses 0.
Break at that point into form where .
First production is .
Thus .
An automaton is an abstract model of a compute
As shown in Linz Figure 1.4, a computer:
reads input from an input file – one symbol from each cell – left to right
produces output
may use storage – unlimited number of cells (may be different alphabet)
has a control unit
A configuration is a state of the control unit, input, and storage.
A move is a transition from one state configuration to another.
Automata can be categorized based on control:
A deterministic automaton has a unique next state from the current configuration.
A nondeterministic automaton has several possible next states.
Automata can also be categorized based on output:
An accepter has only yes/no output.
A transducer has strings or symbols for output,
Various models differ in
how the output is produced
the nature of temporary storage
The syntax rules for identifiers in the language C are as follows:
An identifier is a sequence of letters, digits, and underscores.
An identifier must start with a letter or underscore.
Identifiers allow both uppercase and lowercase letters.
Formally, we can describe these rules with the grammar:
<id> -> <letter><rest> | <underscr><rest>
<rest> -> <letter><rest> | <digit><rest> |
<underscr><rest> | <lambda>
<letter> -> a|b|c|...|z|A|B|C|...|Z
<digit> -> 0|1|2|...|9
<underscr> -> _
Above <lambda>
represents the symbol
,->
is the
for productions, and |
denotes alternative right-hand-sides
of the productions.
The variables are <id>
,
<letter>
, <digit>
,
<underscr>
, and <rest>
. The other
alphanumeric symbols are literals.
Linz Figure 1.6 shows a drawing of an automaton that accepts all legal C identifiers as defined above.
We can interpret the automaton in Linz Figure 1.6 as follows:
<digit>
then the
machine moves to state 3. The machine stops reading with answer No
(non-accepting).<letter>
or
<underscr>
then it moves to state 2. The machine
continues.<letter>
,
<underscr>
, or <digit>
, then the
machine reads the input and remains in state 2.Let where are bits.
Then .
This is the usual binary representation in reverse.
A serial adder process two such numbers x and y, bit by bit, starting at the left end. Each bit addition creates a digit for the sum and a carry bit as shown in Linz Figure 1.7.
A block diagram for the machine is shown in Linz Figure 1.8.
A transducer automaton to carry out the addition of two numbers is shown in Linz Figure 1.9.
The pair on the edges represents the two inputs. The value following the slash is the output.
In chapter 2, we approach automata and languages more precisely and formally than in chapter 1.
A finite automaton is an automaton that has no temporary storage (and, like all automata we consider in this course, a finite number of states and input alphabet).
Linz Definition (DFA): A deterministic finite accepter, or dfa, is defined by the tuple where
Q is a finite set of internal states
is a finite set of symbols called the input alphabet
is a total function called the transition function
is the initial state.
is a set of final states.
currentState :=
To visualize a dfa, we use a transition graph constructed as follows:
The graph pictured in Linz Figure 2.1 represents the dfa , where is represented by
,
,
,
Note that is the initial state and is the only final state in this dfa.
The dfa in Linz Figure 2.1:
What about 0111? 1100?
Linz Definition: The extended transition function is defined recursively:
The extended transition function gives the state of the automaton after reading a string.
Linz Definition 2.2 (Language Accepted by DFA): The language accepted by a dfa is .
That is, is the set of all strings on the input alphabet accepted by automaton M.
Note that above and are total functions (i.e., defined for all strings).
The automaton in Linz Figure 2.2 accepts all strings consisting of arbitrary numbers of ’s followed by a single .
In set notation, the language accepted by the automaton is .
Note that has two self-loop edges, each with a different label. We write this compactly with multiple labels.
A trap state is a state from which the automaton can never “escape”.
Note that is a trap state in the dfa transition graph shown in Linz Figure 2.2.
Transition graphs are quite convenient for understanding finite automata.
For other purposes–such as representing finite automata in programs–a tabular representation of transition function may also be convenient (as shown in Linz Fig. 2.3).
Find a deterministic finite accepter that recognizes the set of all string on starting with the prefix .
Linz Figure 2.4 shows a transition graph for a dfa for this example.
The dfa must accept and then continue until the string ends.
This dfa has a final trap state (accepts) and a non-final trap state (rejects).
Find a dfa that accepts all strings on , except those containing the substring .
need to “remember” whether last two inputs were
use state changes for “memory”
Linz Figure 2.5 shows a dfa for this example.
Accepts: , 0, 00, 01, 010000
Rejects: 001, 000001, 0010101010101
Linz Definition 2.3 (Regular Language): A language L is called regular if and only if there exists a dfa M such that .
Thus dfas define the family of languages called regular.
Show that the language is regular.
Construct a dfa.
Check whether begin/end with “a”.
Am in final state when second a input.
Linz Figure 2.6 shows a dfa for this example.
Question: How would we prove that a languages is not regular?
We will come back to this question in chapter 4.
Let be the language in the previous example (Linz Example 2.5).
Show that is regular.
.
Construct a dfa.
Use Example 2.5 dfa as starting point.
Accept two consecutive strings of form .
Note that any two consecutive ’s could start a second string.
Linz Figure 2.7 shows a dfa for this example.
The last example suggests the conjecture that if a language then so is , , etc.
We will come back to this issue in chapter 4.
Linz Definition 2.4 (NFA): A nondeterministic finite accepter or nfa is defined by the tuple where , , , and are defined as for deterministic finite accepters, but
.
Remember for dfas:
Q is a finite set of internal states.
is a finite set of symbols called the input alphabet.
is the initial state.
is a set of final states.
The key differences between dfas and nfas are
dfa:
yields a single state
nfa:
yields a set of states
dfa: consumes input on each move
nfa: can move without input
()
dfa: moves for all inputs in all states
nfa: some situations have no defined moves
An nfa accepts a string if some possible sequence of moves ends in a final state.
An nfa rejects a string if no possible sequence of moves ends in a final state.
Consider the transition graph shown in Linz Figure 2.8.
Note the nondeterminism in state with two possible transitions for input .
Also state has no transition for any input.
Consider the transition graph for an nfa shown in Linz Figure 2.9.
Note the nondeterminism and the -transition.
Note: Here means the move takes place without consuming any input symbol. This is different from accepting an empty string.
Transitions:
for ?
for ?
for ?
for ?
Accepts: , 10, 1010, 101010
Rejects: 0, 11
As with dfas, the transition function can be extended so its second argument is a string.
Requirement: where is the set of all possible states the automaton may be in, having started in state and read string .
Linz Definition (Extended Transition Function): For an nfa, the extended transition function is defined so that contains if there is a walk in the transition graph from to labelled .
Linz Definition 2.6 (Language Accepted by NFA): The language accepted by the nfa is defined
.
That is, is the set of all strings for which there is a walk labeled from the initial vertex of the transition graph to some final vertex.
Let’s again examine the automaton given in Linz Figure 2.9 (Example 2.8).
This nfa, call it :
must end in
Note that is a dead configuration because .
When computers are deterministic?
an nfa can model a search or backtracking algorithm
nfa solutions may be simpler than dfa solutions (can convert from nfa to dfa)
nondeterminism may model externally influenced interactions (or abstract more detailed computations)
When are two mechanisms (e.g., programs) equivalent?
When they have exactly the same descriptions?
When they always go through the exact same sequence of steps?
When the same input generates the same output for both?
The last seems to be the best approach.
Linz Definition 2.7 (DFA-NFA Equivalence): Two finite accepters and are said to be equivalent if . That is, if they both accept the same language.
Here “same language” refers to the input and “both accept” refers to the output.
Often there are many accepters for a language.
Thus there are many equivalent dfa and nfas.
Again consider the nfa represented by the graph in Linz Fig. 2.9. Call this .
As we saw, .
Now consider the dfa represented by the graph in Linz Figure 2.11. Call this .
.
Thus, is equivalent to .
Which is more powerful, dfa or nfa?
Clearly, any dfa can be made into a nfa .
Can any nfa be made into a dfa ?
Thus, dfas and nfas are “equally powerful”.
Linz Theorem 2.2 (Existence of DFA Equivalent to NFA): Let be the language accepted by the nfa . Then there exists a dfa such that .
A pure mathematician would be content with an existence proof.
But, as computing scientists, we want an algorithm for construction of from . The proof of the theorem follows from the correctness of the following algorithm.
Key ideas:
After reading , will be in the some state from . That is, .
Label the dfa state that has accepted with the set of nfa states . This is an interesting “trick”!
Remember from the earlier definitions in these notes and from discrete mathematics:
is finite (and the same for the nfa and dfa).
is finite.
is a total function. That is, every vertex of the dfa graph has outgoing edges.
The maximum number of dfa states with the above labeling is . Hence, finite.
The maximum number of dfa edges is . Hence, finite.
Procedure nfa_to_dfa
Given a transition graph for nfa , construct a transition graph for a dfa . Label each vertex in with a subset of the vertices in .
Initialize graph to have an initial vertex where is the initial state of .
Repeat the following steps until no more edges are missing from :
Take any vertex from labeled that has no outgoing edge for some .
For this vertex and input, compute . (Each of these is a set of states from .)
Let be the union of all sets formed in the previous step.
If vertex constructed in the previous step (step 2c) is not already in , then add it to .
Add an edge to from vertex (vertex selected in step 2b) to vertex (vertex possibly created in step 2d) and label the new edge with (input selected in step 2b).
Make every vertex of whose label contains any vertex a final vertex of .
If accepts , then vertex in is also a final vertex.
This, if the loop terminates, it constructs the dfa corresponding to the nfa.
Does the loop terminate?
Each iteration of the loop adds one edge to .
There are a finite number of edges possible in .
Thus the loop must terminate.
What is the loop invariant? (This ia a property always that must hold at the loop test.)
Convert the nfa in Linz Figure 2.12 to an equivalent dfa.
Intermediate steps are shown in Figures 2.12-1 and 2.12-2, with the final results in Linz Figure 2.13.
Convert the nfa shown in Linz Figure 2.14 into an equivalent dfa.
The above gives us the partially constructed dfa shown in Linz Figure 2.15.
Now, the above gives us the dfa shown in Linz Figure 2.16.
This section is not covered in this course.
Regular languages
Thus we will examine other notations for representing regular languages.
We define the syntax (or structure) of regular expressions with an inductive definition.
Linz Definition 3.1 (Regular Expression): Let be a given alphabet. Then:
, , and are all regular expressions. These are called primitive regular expressions.
If and are regular expressions, then , , , and are also regular expressions.
A string is a regular expression if and only if it can be derived from the primitive regular expressions by a finite number of applications of the rules in (2).
We use the the regular expression operators as follows:
represents the union of two regular expressions.
is the concatenation of two regular expressions.
is the star closure of a regular expression.
is the same as regular expression . It is parenthesized to express the order of operations explicitly.
For example, consider regular expression over the alphabet . Note the use of parentheses.
, , and are primitive regular expressions.
is a concatenation of regular expressions and .
is union of regular expressions and .
is the star-closure of regular expression .
As with arithmetic expressions, precedence rules and conventions can be used to relax the need for parentheses.
Star-closure () has a higher precedence (i.e., priority or binding power) than concatenation (). That is, is equal to , not .
Concatenation () higher precedence than union (). That is, is equal to , not . And, transitively, star-closure has a higher precedence than concatenation.
Concatenation operator () can usually be omitted. That is, means .
A string is not a regular expression. It cannot be generated using the above definition (as augmented by the precedence rules and convention).
But what do we “mean” by a regular expression? That is, what is its semantics.
In particular, what languages do regular expressions describe?
Consider the regular expression from above. As implied by the names for the operators, we intend this regular expression to represent the language which is .
We again give an inductive definition for the language described by a regular expression. It must consider all the cases given in the definition of regular expression itself.
Linz Definition 3.2: The language denoted by any regular expression is defined (inductively) by the following rules.
Base cases:
Inductive cases: If and are regular expressions, then
Show the language in set notation.
{ Rule 5 } | |
{ Rule 7 } | |
{ Rule 4 } | |
{ definition of star-closure of languages } | |
{ definition of concatenation of languages } | |
Consider the languages for the following regular expressions.
Consider the regular expression .
This expression denotes the set of all strings with an even number of ’s followed by an odd number of ’s.
In set notation, .
Show regular expressions on the alphabet for the following languages.
Regular expressions provide a convenient and concise notation for describing regular languages.
Linz Theorem 3.1 (NFAs for Regular Expressions): Let be a regular expression. Then there exists some nondeterministic finite accepter (nfa) that accepts . Consequently, is a regular language.
Proof Sketch: Show that any regular expression generated from the inductive definition corresponds to an nfa. Here we proceed informally.
Linz Figure 3.1 diagrammatically demonstrates that there are nfas that correspond to the primitive regular expressions.
Linz Figure 3.2 shows a general scheme for a nondeterministic finite accepter (nfa) that accepts , with an initial state and one final state.
Linz Figure 3.3 gives an nfa for . Note the use of -transitions to connect the two machines to the new initial and final states.
Linz Figure 3.4 shows an nfa for . Again note the use of -transitions to connect the two machines to the new initial and final states.
Linz Figure 3.5 shows an nfa for . Note the use of -transitions to represent zero-or-more repetitions of the machine and to connect it to the new initial and final states.
Thus, Linz Figures 3.3 to 3.5 illustrate composing nfas for any regular expression from the nfas for its subexpressions. Of course, the initial and final states of components are replaced by the initial and final states of the composite nfa.
Show an nfa that accepts .
Linz Figure 3.6, part (a), shows that accepts . Part (b) shows that accepts .
Linz Figure 3.7 shows an nfa that accepts .
The construction in the proof sketch and example above suggest an algorithm for converting regular expressions to nfas.
This algorithm is adapted from pages 273-4 of the book: James L. Hein, Theory of Computation: An Introduction, Jones and Bartlett, 1996.
The diagrams in this section are from the Hein book, which uses a slightly different notation than the Linz book. In particular, these diagrams use capital letters for the expressions.
Algorithm to convert a regular expression to an nfa
Start with a “machine” with a single start state, a single final state, and a connecting edge labeled with the regular expression.
While there are edges labeled with regular expressions other than elements of the alphabet or apply any of the following rules that are applicable:
If an edge is labeled with , then remove the edge.
If an edge is labeled with , then replace the edge with two edges labeled with and connecting the same source and destination states.
If an edge is labeled with , the replace the edge with an edge labeled connecting the source to a new intermediate state, followed by an edge labeled connecting the intermediate state to the destination.
If an edge is labeled with , then replace the edge with a new intermediate state with a self-loop labeled with edges labeled connecting the source to the intermediate state and the intermediate state to the destination.
End of Algorithm
Rule 2 in the above algorithm can result in an unbounded number of edges originating at the same state. This makes the algorithm difficult to implement. To remedy this situation, replace Rule 2 as follows.
If an edge is labeled with , then replace the edge with subgraphs for each of and . The subgraph for consists of with a new source state connected to a new destination state with an edge labeled . Add edges labeled to connect the original source state to the new source state and the original destination state to the new destination state. Proceed similarly for .
This example is from page 275 of the Hein textbook cited above.
Construct an nfa for .
Start with a the two-state initial diagram.
Next, apply Rule 2 to .
Next, apply Rule 4 to .
Finally, apply Rule 3 to .
The construction in the proof sketch and example above suggest an algorithm for converting finita automata to regular expressions.
This algorithm is adapted from page 276 of the book: James L. Hein, Theory of Computation: An Introduction, Jones and Bartlett, 1996.
Algorithm to convert a finite automaton to a regular expression
Begin with a dfba or an nfa.
Create a new start state and connect this to the original start state with an edge labeled .
Create a new final state and connect the original final states to this state by edges labeled .
For each pair of states and that has more than one edge connecting them, replace all the edges with the regular expression formed using union () to combine the labels on the previous edges.
Construct a sequence of new machines by eliminating one state at a time until the only states remaining are and . To eliminate some state , construct a new machine as follows.
Let old) represent the label on the edge on the current (i.e., old) machine.
If there is no edge , then set old.
For every pair of edges and , where and , calculate a new edge label new as follows:
new old old old old
For all other edges , where and , set:
new old.
The states of the new machine are the states of the old machine with state eliminated. The edges of the new machine are the where the new has been calculated.
After eliminating all states except and , the regular expression is the label on the one edge remaining.
End of Algorithm
This example is from pages 277-8 of the Hein textbook cited above.
Consider the following dfa.
After applying Rule 1 (new start state), Rule 2 (new final state), and Rule 3 (create union), we get the following machine.
We can eliminate state 2 readily because it is trap state. That is, there is no path through 2 between edges adjacent to 2, so new old for any states and . The resulting machine is as follows.
To eliminate state 0, we construct a new edge that is labeled as follows:
Thus, we can eliminate state 0 and its edges and add a new edge labeled .
We can eliminate state 1 by adding a new edge labeled as follows
Thus the regular expression is .
This example is from pages 277-8 of the Hein textbook cited above.
Consider the following dfa. Verify that it corresponds to the regular expression .
Applying Rules 1 and 2 (adding new start and final states), we get the following machine.
To eliminate state 0, we add the following new edges.
new
new
We can eliminate either state 2 or state 3 next. Let’s choose 3. Thus we create the following new edges.
new
new
Now we eliminate state 2 and thus create the new edges.
new
new
Finally, we remove state 1 by creating a new edge.
Pascal integer constants
Regular expression where
Pattern matching
Program for Pattern Matching
We can convert a regular expression to an equivalent nfa, the nfa to a dfa, and the dfa to a transition table. We can use the transition table to drive a program for pattern matching.
For a more effiicent program, we can apply the state reduction algorithm to the dfa before converting to a transition table. Linz section 2.4, which we did not cover this semester, discusses this algorithm.
We have studied two ways of describing regular languages–finite automata (i.e. dfas, nfas) and regular expressions. Here we examine a third way–regular grammars.
Linz Definition 3.3 (Right-Linear Grammar): A grammar is said to be right-linear if all productions are of one of the forms
where and .
Similarly, a grammar is said to be left-linear if all productions are of the form or .
A regular grammar is one that is either right-linear or left-linear.
The grammar , with given as
is right-linear.
The grammar , with productions
is left linear. Both and are regular grammars.
The grammar with productions
is not regular.
Although every production is either in right-linear or left-linear form, the grammar itself is neither right-linear nor left-linear, and therefore is not regular. The grammar is an example of a linear grammar.
Definition (Linear Grammar): A linear grammar is a grammar in which at most one variable can appear on the right side of any production.
Linz Theorem 3.3 (Regular Languages for Right-Linear Grammars): Let be a right-linear grammar. Then is a regular language.
Strategy: Because a regular language is any language accepted by a dfa or nfa, we seek to construct an nfa that simulates the derivations of the right-linear grammar.
The algorithm below incorporates this idea. It is based on the algorithm given on page 314 of the book: James L. Hein, Theory of Computation: An Introduction, Jones and Bartlett, 1996.
Algorithm to convert a regular grammar to an nfa
Start with a right-linear grammar and construct an equivalent nfa. We label the nfa’s states primarily with variables from the grammar and label edges with terminals in the grammar or .
If necessary, transform the grammar so that all productions have the form or , where is either a terminal in the grammar or .
Label the start state of the nfa with the start symbol of the grammar.
For each production , add a state transition (edge) from a state to a state with the edge labeled with the symbol .
For each production , add a state transition (edge) from a state to a state with the edge labeled with .
If there exist productions of the form , then add a single new state symbol . For each production of the form , add a state transition from to labeled with symbol .
The final states of the nfa are plus all such there is a production of the form .
End of algorithm
Construct an nfa for the following regular grammar :
The grammar is in the correct form, so step 1 of the grammar is not applicable. The following sequence of diagrams shows the use of steps 2, 3 (three times), 5, and 6 of the algorithm. Step 4 is not applicable to this grammar.
Note that .
This is similar to the example in the Linz textbook, but we apply the algorithm as stated above.
Construct an nfa for the regular grammar :
First, let’s transform the grammar according to step 1 of the regular grammar to nfa algorithm above.
Applying steps 2, 3 (three times), 5, and 6 of algorithm as show below, we construct the following nfa. Step 4 was not applicable in this problem.
Note that .
Linz Theorem 3.4 (Right-Linear Grammars for Regular Languages): If is a regular language on the alphabet , then there exists a right-linear grammar such that .
Strategy: Reverse the construction of an nfa from a regular grammar given above.
The algorithm below incorporates this idea. It is based on the algorithm given on page 312 of the Hein textbook cited above.
Algorithm to convert an nfa to a regular grammar
Start with an nfa and construct a regular grammar.
Relabel the states of the nfa with capital letters.
Make the start state label the start symbol for the grammar.
For each transition (edge) from a state to a state labeled with an alphabetic symbol , add a production to the grammar.
For each transition (edge) from a state to a state labeled with , add a production to the grammar.
For each final state labeled , add a production to the grammar.
End of algorithm
Consider the following nfa (adapted from the Hein textbook page 313). (The Hein book uses instead of to label silent moves and empty strings.)
We apply the steps of the algorithm as follows.
The nfa states are already labeled as specified.
Choose as start symbol for grammar.
Add the following productions:
Add the following production:
Add the following production:
So, combining the above productions, we get the final grammar:
Linz Theorem 3.5 (Equivalence of Regular Languages and Left-Linear Grammars): A language is regular if and only if there exists a left-linear grammar such that .
Linz Theorem 3.6(Equivalence of Regular Languages and Right-Linear Grammars): A language is regular if and only if there exists a regular grammar such that .
The four theorems from this section enable us to convert back and forth among finite automata and regular languages as shown in Linz Figure 3.19. Remember that Linz Theorem 2.2 enabled us to translate from nfa to dfa.
The questions answered in this chapter include:
The concepts introduced in this chapter are:
Definition (Operation): An operation is a function where for some sets with . is the number of operands (or arguments) of the operation.
We often use special notation and conventions for unary and binary operations. For example:
a binary operation may be written in an infix style as in and
a unary operation may be written in a prefix style as in , suffix style such as , or special style such as or
a binary operation may be implied by the juxtaposition such as for multiplication or (in a different context) for string concatenation or implied by superscripting such as for exponentiation
Often we consider an operations on a set, where all the operands and the result are drawn from the same set.
Definition (Closure): A set is closed under a unary operation if, for all , . Similarly, a set is closed under a binary operation if, for all and , .
Examples arithmetic on the set of natural numbers ()
Binary operations addition () and multiplication ( in programming languages) are closed on
Binary operations subtraction () and division () are not closed on
For example,
is not a natural number.
For example,
is not a natural number.
Unary operation negation (operator written in prefix form) is not closed on .
However, the set of integers is closed under subtraction and negation. But it is not closed under division or square root (as we normally define the operations).
Now, let’s consider closure of the set of regular languages with respect to the simple set operations.
Linz Theorem 4.1 (Closure under Simple Set Operations): If and are regular languages, then so are , , , , and .
That is, we say that the family of regular languages is closed under union, intersection, concatenation, complementation, and star-closure.
Proof of
Let and be regular languages.
{ Th. 3.2: there exist regular expressions , } | |
{ Def. 3.2, rule 4 } | |
Thus, by Theorem 3.1 (regular expressions describe regular languages), the union is a regular language.
Thus is a regular language. QED.
Proofs of and
Similar to the proof of .
Proof of
Strategy: Given a dfa for the regular language, construct a new dfa that accepts everything rejected and rejects everything accepted by the given dfa.
is a regular language on . | |
{ Def. 2.3 } | |
dfa such that . |
Thus
{ by the properties of dfas and sets } | |
Either or | |
{ Def. 2.2: language accepted by dfa } | |
Either or for some dfa |
Let’s construct dfa .
Clearly, . Thus is a regular language. QED.
Proof of
Strategy: Given two dfas for the two regular languages, construct a new dfa that accepts a string if and only if both original dfas accept the string.
Let and for dfas:
Construct , where
- when
Clearly, if and only if accepted by .
Thus, is regular. QED.
The previous proof is constructive.
It establishes desired result.
It provides an algorithm for building an item of interest (e.g., dfa to accept ).
Sometimes nonconstructive proofs are shorter and easier to understand. But they provide no algorithm.
Alternate (nonconstructive) proof for
and are regular. | |
{ previously proved part of Theorem 4.1 } | |
and are regular. | |
{ previously proved part of Theorem 4.1 } | |
is regular | |
{ previously proved part of Theorem 4.1 } | |
is regular | |
{ deMorgan’s Law for sets } | |
is regular |
QED.
Consider the difference between two regular languages and , written .
But this is just set difference, which is defined .
From Theorem 4.1 above, we know that regular languages are closed under both complementation and intersection. Thus, regular languages are closed under difference as well.
Linz Theorem 4.2 (Closure under Reversal): The family of regular languages is closed under reversal.
Proof (constructive)
Strategy: Construct an nfa for the regular language and then reverse all the edges and exchange roles of the initial and final states.
Let be a regular language. Construct an nfa such that and has a single final state. (We can add transitions from the previous final states to create a single new final state.)
Now construct a new nfa as follows.
Thus nfa accepts if and only if the original nfa accepts . QED.
In mathematics, a homomorphism is a mapping between two mathematical structures that preserves the essential structure.
Linz Definition 4.1 (Homomorphism): Suppose and are alphabets. A function
is called a homomorphism.
In words, a homomorphism is a substitution in which a single letter is replaced with a string.
We can extend the domain of a function to strings in an obvious fashion. If
for
then
.
If is a language on , then we define its homomorphic image as
.
Note: The homomorphism function preserves the essential structure of the language. In particular, it preserves operation concatenation on strings, i.e., and .
Let and .
Define as follows:
,
Then .
The homomorphic image of is the language .
If we have a regular expression for a language , then a regular expression for can be obtained by simply applying the homomorphism to each symbol of . We show this in the next example.
For and , define :
If is a regular language denoted by the regular expression
then
denotes the regular language .
The general result on the closure of regular languages under any homomorphism follows from this example in an obvious manner.
Linz Theorem 4.3 (Closure under Homomorphism): Let be a homomorphism. If is a regular language, then its homomorphic image is also regular.
Proof: Similar to the argument in Example 4.3. See Linz textbook for full proof.
The family of regular languages is therefore closed under arbitrary homomorphisms.
Linz Definition 4.2 (Right Quotient): Let and be languages on the same alphabet. Then the right quotient of with is defined as
for some
Given languages and such that
Then
.
The strings in consist of one or more ’s. Therefore, we arrive at the answer by removing one or more ’s from those strings in that terminate with at least one as a suffix.
Note that in this example , , and are regular.
Can we construct a dfa for from dfas for and ?
Linz Figure 4.1 shows a dfa that accepts .
An automaton for must accept any such that and .
For all states , if there exists a walk labeled from to a final state such that , then make a final state of the automaton for .
In this example, we check states to see if there is walk to any of the final states , , or .
and have such walks.
, , and do not.
The resulting automaton is shown in Linz Figure 4.2.
The next theorem generalizes this construction.
Linz Theorem 4.4 (Closure under Right Quotient): If and are regular languages, then is also regular. We say that the family of regular languages is closed under right quotient with a regular language.
Proof
Let dfa such that .
Construct dfa for as follows.
For all , let dfa . That is, dfa is the same as except that it starts at .
From Theorem 4.1, we know is regular. Thus we can construct the intersection machine as show in the proof of Theorem 4.1.
If there is any path in the intersection machine from its initial state to a final state, then . Thus in machine .
Does ?
First, let .
By definition, there must be such that .
Thus .
There must be some such that and .
Thus, by construction, . Hence, accepts .
Now, let be accepted by .
.
Thus, by construction, we know there is a such that .
Thus , which means is regular.
Find for
We apply the construction (algorithm) used in the proof of Theorem 4.4.
Linz Figure 4.3 shows a dfa for .
Let .
Thus if we construct the sequence of machines
then the resulting dfa for is shown in Linz Figure 4.4.
The automaton shown in Figure 4.4 accepts the language denoted by the regular expression
which can be simplified to
Fundamental question: Is ?
It is difficult to find a membership algorithm for languages in general. But it is relatively easy to do for regular languages.
A regular language is given in a standard representation if and only if described with one of:
Linz Theorem 4.5 (Membership): Given a standard representation of any regular language on and any there exists an algorithm for determining whether or not is in .
Proof
We represent the language by some dfa, then test to see if it is accepted by this automaton. QED.
Linz Theorem 4.6 (Finiteness): There exists an algorithm for determining whether a regular language, given in standard representation, is empty, finite, or infinite.
Proof
Represent as a transition graph of a dfa.
If simple path exists from the initial state to any final state, then it is not empty. Otherwise, it is empty.
If any vertex on a cycle is in a path from the initial state to any final state, then the language is infinite. Otherwise, it is finite.
QED.
Consider the question ?
This is practically important. But it is a difficult issue because there are many ways to represent and .
Linz Theorem 4.7 (Equality): Given a standard representation of two regular languages and , there exists an algorithm to determine whether or whether not .
Proof
Let .
By closure, is regular. Hence, there is a dfa that accepts .
Because of Theorem 4.6, we can determine whether is empty or not.
But from Excerise 8, Section 1.1, we see that if and only if . QED.
A regular languages may be infinite
In processing a string, the amount of information that the automaton must “remember” is strictly limited (finite and bounded).
In mathematics, the pigeonhole principle refers to the following simple observation:
If we put objects into boxes (pigeonholes), and, if , at least one box must hold more than one item.
This is obvious, but it has deep implications.
Is the language regular?
The answer is no, as we show below.
Proof that is not regular
Strategy: Use proof by contradiction. Assume that what we want to prove is false. Show that this introduces a contradiction. Hence, the original assumption must be true.
Assume is regular.
Thus there exists a dfa such that .
Machine has a specific number of states. However, the number of ’s in a string in is finite but unbounded (i.e., no maximum value for the length). If is larger than the number of states in , then, according to the pigeonhole principle, there must be some state such that
and
with . But, because accepts ,
for some .
From this we reason as follows:
But this contradicts the assumption that accepts only if . Therefore, cannot be regular. QED
We can use the pigeonhole principle to make “finite memory” precise.
Linz Theorem 4.8 (Pumping Lemma for Regular Languages): Let be an infinite regular language. There exists some such that any with can be decomposed as
with
and
such that
is also in for all .
That is, we can break every sufficiently long string from into three parts in such a way that an arbitrary number of repetitions of the middle part yields another string in .
We can “pump” the middle string, which gives us the name pumping lemma for this theorem.
Proof
Let be an infinite regular language. Thus there exists a dfa that accepts . Let have states .
Consider a string such that . Such a string exists because is infinite.
Consider the set of states that traverses as it processes .
The size of this set is exactly . Thus, according to the pigeonhole principle, at least one state must be repeated, and such a repetition must start no later than the th move.
Thus the sequence is of the form
.
Then there are substrings , , and of such that
with and . Thus, for any ,
QED.
We can use the pumping lemma to show that languages are not regular. Each of these is a proof by contradiction.
Show that is not regular.
Assume that is regular, so that the Pumping Lemma must hold.
If, for some and , and are both in , then must be all ’s or all ’s.
We do not know what is, but, whatever is, the Pumping Lemma enables us to choose a string . Thus must consist entirely of ’s.
Suppose . We must decompose as follows for some :
From the Pumping Lemma
.
Clearly, this is not in . But this contradicts the Pumping Lemma.
Hence, the assumption that is regular is false. Thus is not regular.
The Pumping Lemma guarantees the existence of and decomposition for any string in a regular language.
But we do not know what and are.
We do not have contradiction if the Pumping Lemma is violated for some specific or .
The Pumping Lemma holds for all and for all (i.e., for all ).
We can thus conceptualize a proof as a game against an opponent.
Our goal: Establish a contradiction of the Pumping Lemma.
Opponent’s goal: Stop us.
Moves:
The opponent picks .
Given , we pick a string in of length equal or greater than . We are free to choose any , subject to requirement and .
The opponent chooses the decomposition , subject to and . We have to assume that the opponent makes the choice that will make it hardest for us to win the game.
We try to pick in such a way that the pumped string , as defined in , is not in . If we can do so, we win the game.
Strategy:
Let . Show that
is not regular.
We use the Pumping Lemma and assume is regular.
Whatever the opponent picks in step 1 (of the “game”), we can choose a as shown below in step 2.
Because of this choice, and the requirement that , in step 3 the opponent must choose a that consists entirely of ’s. Consider
that must hold because of the Pumping Lemma.
In step 4, we use in . This string has fewer ’s on the left than on the right and so cannot be of the form .
Therefore, the Pumping Lemma is violated. is not regular.
Warning: Be careful! There are ways we can go wrong in applying the Pumping Lemma.
If we choose too short in step 2 of this example (i.e., where the first symbols include two or more ’s), then the opponent can choose a having an even number of ’s. In that case, we could not have reached a violation of the pumping lemma on the last stap.
If we choose a string consisting of all ’s, say
which is in . To defeat us, the opponent need only pick
Now is in for all , and we lose. ^
We must assume the opponent does not make mistakes. If, in the case where we pick , the opponent picks
then is a string of odd length and therefore not in . But any argument is incorrect if it assumes the opponent fails to make the best possible choice (i.e., ).
For , show that the language
is not regular.
We use the Pumping Lemma to show a contradiction. Assume
is
regular.
Suppose the opponent gives us . Because we have complete freedom in choosing , we pick . Now, because cannot be greater than , the opponent cannot do anything but pick a with all ’s, that is,
for .
We now pump up, using . The resulting string
is not in . Therefore, the Pumping Lemma is violated. is not regular.
Show that
is not regular
We use the Pumping Lemma to show a contradiction. Assume
is
regular.
Given some , we pick as our string
which is in .
The opponent must decompose so that and . Thus both and must be in the part of the string consisting of pairs. The choice of does not affect the argument, so we can focus on the part.
If our opponent picks , we can choose and get a string not in and, hence, not in . (There is a similar argument for .)
If the opponent picks , we can choose again. Now we get the string , which is not in . (There is a similar argument for .)
In a similar manner, we can counter any possible choice by the opponent. Thus, because of the contradiction, is not regular.
Note: This example is adapted from an earlier edition of the Linz textbook.
Show that
is not regular.
We use the Pumping Lemma to show a contradiction. Assume is regular.
Given the opponent’s choice for , we pick to be the string (unless the opponent picks , in which case we can use as ).
The possible decompositions (such that ) differ only in the lengths of and . Suppose the opponent picks such that
.
According to the Pumping Lemma, . But this string can only be in if there exists a such that
.
But this is impossible, because for and we know (see argument below) that
.
Therefore, is not regular.
Aside: To see that for and , note that
.
Show that the language
is not regular.
Strategy: Instead of using the Pumping Lemma directly, we show that is related to another language we already know is nonregular. This may be an easier argument.
In this example, we use the closure property under homomorphism (Linz Theorem 4.3).
Let be defined such that
.
Then
But we proved this language was not regular in Linz Example 4.6. Therefore, because of closure under homomorphism, cannot be regular either.
Alternative proof by contradiction
Assume is regular.
Thus is regular by closure under homomorphism (Linz Theorem 4.3).
But we know is not regular, so there is a contradiction.
Thus, is not regular.
Show that the language
is not regular.
We use the Pumping Lemma, but this example requires more ingenuity to set up than previous examples.
Assume is regular.
Choosing a string with or will not lead to a contradiction.
In these cases, the opponent can always choose a decomposition (with and ) that will make it impossible to pump the string out of the language (that is, pump it so that it has an equal number of ’s and ’s). For , the opponent can chose to be an even number of ’s. For , the opponent can chose to be an odd number of ’s greater than 1.
We must be more creative. Suppose we choose where and .
If the opponent decomposes (with and ), then must consist of all ’s.
If we pump times, we generate string where the number of ’s is
We can contradict the Pumping Lemma if we can pick such that
.
But we can do this, because it is always possible to choose
.
For , the expression is an integer.
Thus the generated string has occurrences of .
This introduces a contradiction of the Pumping Lemma. Thus is not regular.
Alternative argument (more elegant)
Suppose is regular.
Because of complementation closure, is regular.
Let .
But is regular and thus, by intersection closure, is also regular.
But , which we have shown to be nonregular. Thus we have a contradiction, so is not regular.
The Pumping Lemma is difficult to understand and, hence, difficult to apply.
Here are a few suggestions to avoid pitfalls in use of the Pumping Lemma.
Do not attempt to use the Pumping Lemma to show a language is regular. Only use it to show a language is not regular.
Make sure you start with a string that is in the language.
Avoid invalid assumptions about the decomposition of a string into . Use only that and .
Like most interesting “games”, knowledge of the rules for use of the Pumping Lemma is necessary, but it is not sufficient to become a master “player”. To master the use of the Pumping Lemma, one must work problems of various difficulties. Practice, practice, practice.
In Linz Section 4.3, we saw that not all languages are regular. We examined the Pumping Lemma for Regular Languages as a means to prove that a specific language is not regular.
In Linz Example 4.6, we proved that
is not regular.
If we let = “(” and = “)”, then becomes a language of nested parenthesis.
This language is in a larger family of languages called the context-free languages.
Context-free languages are very important because many practical aspects of programming languages are in this family.
In this chapter, we explore the context-free languages, beginning with context-free grammars.
One key process we explore is parsing, which is the process of determining the grammatical structure of a sentence generated from a grammar.
Remember the restrictions we placed on regular grammars in Linz Section 3.3:
To create more powerful grammars (i.e., that describe larger families of languages), we relax these restrictions.
For context-free grammars, we maintain the left-side restriction but relax the restriction on the right side.
Linz Definition 5.1 (Context-Free Grammar): A grammar is context-free if all productions in have the form
where and . A language is context-free if and only if there is a context-free grammar such that .
The family of regular languages is a subset of the family of context-free languages!
Thus, context-free grammars
enable the right side of a production to be substituted for a variable on the left side at any time in a sentential form
with no dependencies on other symbols in the sentential form.
Consider the grammar with productions:
Note that this grammar satisfies the definition of context-free.
A possible derivation using this grammar is as follows:
From this derivation, we see that
.
The language is context-free, but, as we demonstrated in Linz Example 4.8, it is not regular.
This grammar is linear because it has at most one variable on the right.
Consider the grammar with productions:
Note that this grammar also satisfies the definition of context free.
A possible derivation using this grammar is:
We can see that:
This grammar is also linear (as defined in Linz Section 3.3). Although linear grammars are context free, not all context free grammars are linear.
Consider the language
.
This language is context free. To show that this is the case, we must construct a context-free grammar that generates the language
First, consider the case. This can be generated by the productions:
Now, consider the case. We can modify the above to generate extra ’s on the left.
Finally, consider the case. We can further modify the grammar to generate extra ’s on right.
This grammar is context free, but it is not linear because the productions with on the left are not in the required form.
Although this grammar is not linear, there exist other grammars for this language that are linear.
Consider the grammar with productions:
This grammar is also context-free but not linear.
Example strings in include , , and . Note that:
and are always generated in pairs.
precedes the matching .
A prefix of a string may contain several more ’s than ’s.
We can see that is
{ and for any prefix of }.
What is a programming language connection for this language?
Let = “(” and = “)”.
This gives us a language of properly nested parentheses.
Consider grammar with productions:
This grammar generates the language .
Now consider the two derivations:
These derivations yield the same sentence using exactly the same productions. However, the productions are applied in different orders.
To eliminate such incidental factors, we often require that the variables be replaced in a specific order.
Linz Definition 5.2 (Leftmost and Rightmost Derivations): A derivation is leftmost if, in each step, the leftmost variable in the sentential form is replaced. If, in each step, the rightmost variable is replaced, then the derivation is rightmost.
Consider the grammar with productions:
A leftmost derivation of the string is:
Similarly, a rightmost derivation of the string is:
We can also use a derivation tree to show derivations in a manner that is independent of the order in which productions are applied.
A derivation tree is an ordered tree in which we label the nodes with the left sides of productions and the children of a node represent its corresponding right sides.
The production
is shown as a derivation tree in Linz Figure 5.1.
Linz Definition 5.3 (Derivation Tree): Let be a context-free grammar. An ordered tree is a derivation tree for if and only if it has the following properties:
The root is labeled .
Every leaf has a label from .
Every interior vertex (i.e., a vertex that is not a leaf) has a label from .
If a vertex has a label , and its children are labeled (from left to right) , then must contain a production of the form .
A leaf labeled has no siblings, that is, a vertex with a child labeled can have no other children.
If properties 3, 4, and 5 and modified property 2 (below) hold for a tree, then it is a partial derivation tree.
If we read the leaves of a tree from left to right, omitting any ’s encountered, we obtain a string called the yield of the tree.
The descriptive term from left to right means that we traverse the tree in a depth-first manner, always exploring the leftmost unexplored branch first. The yield is the string of terminals encountered in this traversal.
Consider the grammar with productions:
Linz Figure 5.2 shows a partial derivation tree for with the yield . This is a sentential form of the grammar .
Linz Figure 5.3 shows a derivation tree for with the yield . This is a sentence of .
Derivation trees give explicit (and visualizable) descriptions of derivations. They enable us to reason about context-free languages much as transition graphs enable use to reason about regular languages.
Linz Theorem 5.1 (Connection between Derivations and Derivation Trees): Let be a context-free grammar. Then the following properties hold:
For every , there exists a derivation tree of whose yield is .
The yield of any derivation tree of is in .
If is any partial derivation tree for whose root is labeled , then the yield of is a sentential form of .
Proof: See the proof in the Linz textbook.
Derivation trees:
show which productions are used to generate a sentence
abstract out the order in which individual productions are applied
enable the construction of eiher a leftmost or rightmost derivation
The previous section concerns the generative aspects of grammars–using a grammar to generate strings in the language.
This section concerns the analytical aspects of grammars–processing strings from the language to determine their derivations. This process is called parsing.
For example, a compiler for a programming language must parse a program (i.e., a sentence in the language) to determine the derivation tree for the program.
This verifies that the program is indeed in the language (syntactically).
Construction of the derivation tree is needed to execute the program (e.g., to generate the machine-level code corresponding to the program).
Given some , we can parse with respect to grammar by:
systematically constructing all derivations
determining whether any derivation matches
This is called exhaustive search parsing or brute force parsing. A more complete description of the algorithm is below.
This is a form of top-down parsing in which a derivation tree is constructed from the root downward.
Note: An alternative approach is bottom-up parsing in which the derivation tree is constructed from leaves upward. Bottom-up parsing techniques often have limitations in terms of the grammars supported but often give more efficient algorithms.
Exhaustive Search Parsing Algorithm
Note: The above algorithm determines whether a string is in . It can be modified to build the actual derivation or derivation tree.
Note: The presentation here uses the algorithm above, rather than the approach in the Linz textbook.
Consider the grammar with productions:
Parse the string .
After initialization: (from the righthand sides of the grammar’s four productions with on the left).
First iteration: The loop test is true because is nonempty and is not present.
The algorithm does not need to consider the sentential forms and in because neither can generate .
The inner loop thus adds from the leftmost derivations from sentential form and also adds from the leftmost derivations from sentential form .
Thus at the end of the first iteration.
Second iteration: The algorithm enters the loop a second time because is nonempty and does not contain .
The algorithm does not need to consider any sentential form beginning with or , thus eliminating and leaving of interest.
This iteration generates 20 new sentential forms from applying each of the 4 productions to each of the 5 remaining sentential forms.
In particular, note that that sentential form yields the target string when production is applied.
Third iteration: The loop terminates because is present in .
Thus we can conclude .
Exhaustive search parsing has serious flaws:
It is tedious and inefficient.
It might not terminate when .
For example, if we choose in the previous example, the algorithm goes into an infinite loop.
The fix for nontermination is to ensure sentential forms increase in length for each production. That is, we eliminate productions of forms:
Chapter 6 of the Linz textbook (which we will not cover this semester) shows that this does not reduce the power of the grammar.
Consider the grammar with productions:
This grammar generates the same language as the one in Linz Example 5.7 above, but it satisfies the restrictions given in the previous subsection.
Given any nonempty string , exhaustive search will terminate in no more than rounds for such grammars.
Linz Theorem 5.2 (Exhaustive Search Parsing): Suppose that is a context-free grammar that does not have any rules of one of the forms
where . Then the exhaustive search parsing method can be formulated as an algorithm which, for any , either parses or tells us that parsing is impossible.
Proof outline
Each production must increase either the length or number of terminals.
The maximum length of a sentential form is , which is the maximum number of terminal symbols.
Thus for some , the number of loop iterations is at most .
But exhaustive search is still inefficient. The number of sentential forms to be generated is
.
That is, it grows exponentially with the length of the string.
Linz Theorem 5.3 (Efficient Parsing): For every context-free grammar there exists an algorithm that parses any in a number of steps proportional to .
Construction of more efficient context-free parsing methods is left to compiler courses.
is still inefficient.
We would prefer linear () parsing.
Again we must restrict the grammar in our search for more efficient parsing. The next subsection illustrates on such grammar.
Linz Definition 5.4 (Simple Grammar): A context-free grammar is said to be a simple grammar or s-grammar if all its productions are of the form
where , and any pair occurs at most once in .
The grammar
is an s-grammar.
The grammar
is not an s-grammar because occurs twice.
Although s-grammars are quite restrictive, many features of programming languages can be described with s-grammars (e.g., grammars for arithmetic expressions).
If is s-grammar, then can be parsed in linear time.
To see this, consider string and use the exhaustive search parsing algorithm.
The s-grammar has at most one rule with on left: . Choose it!
Then the s-grammar has at most one rule with on left: . Choose it!
And so forth up to the th terminal.
The number of steps is proportional to because each step consumes one symbol of .
A derivation tree for some string generated by a context-free grammar may not be unique.
Linz Definition 5.5 (Ambiguity): A context-free grammar is said to be ambiguous if there exists some that has at least two distinct derivation trees. Alternatively, ambiguity implies the existence of two or more leftmost or rightmost derivations.
Again consider the grammar in Linz Example 5.4. Its productions are
.
The string has two derivation trees as shown in Linz Figure 5.4
The left tree corresponds to the leftmost derivation .
The right tree corresponds to the leftmost derivation .
Thus the grammar is ambiguous.
Consider the grammar with
and including the productions:
This grammar generates a subset of the arithmetic expressions for a language like C or Java. It contains strings such as and .
Linz Figure 5.5 shows two derivation trees for the string . Thus this grammar is ambiguous.
Why is ambiguity a problem?
Remember that the semantics (meaning) of the expression is also associated with the structure of the expression. The structure determines how the (machine language) code is generated to carry out the computation.
How do real programming languages resolve this ambiguity?
Often, they add precedence rules that give priority to “” over “”. That is, the multiplication operator binds more tightly than addition.
This solution is totally outside the world of the context-free grammar. It is, in some sense, a hack.
A better solution is to rewrite the grammar (or sometimes redesign te language) to eliminate the ambiguity.
To rewrite the grammar in Linz Example 5.11, we introduce new variables, making the set , and replacing the productions with the following:
Linz Figure 5.6 shows the only derivation tree for string in this revised grammar for arithmetic expressions.
Linz Definition 5.6: If is a context-free language for which there exists an unambiguous grammar, then is said to be unambiguous. If every grammar that generates is ambiguous, then language is called inherently ambiguous.
It is difficult to demonstrate that a grammar is inherently ambiguous. Often the best we can do is to give examples and argue informally that all grammars must be ambiguous.
The language
,
with and non-negative, is an inherently ambiguous context-free language.
Note that .
We can generate with the context-free grammar:
Similarly, we can generate with the context-free grammar:
We can thus construct the union of these two sublanguages by adding a new production:
Thus this is a context-free language.
But consider a string of the form (i.e., ). It has two derivations, one starting with
and another starting with
.
Thus the grammar is ambiguous.
and have conflicting requirements. places restrictions on the number of ’s and ’s while places restrictions on the number of ’s and ’s. It is imposible to find production rules that satisfy the case uniquely.
The syntax for practical programming language syntax is usually expressed with context-free grammars. Compilers and interpreters must parse programs in these language to execute them.
The grammar for programming languages is often expressed using the Backus-Naur Form (BNF) to express productions.
For example, the language for arithmetic expressing in Linz Example 5.12 can be written in BNF as:
<expression> ::= <term> | <expression> + <term>
<term> ::= <factor> | <term> * <factor>
The items in angle brackets are variables, the symbols such as “+” and “-” are terminals, the “|” denotes alternatives, and “::=” separates the left and right sides of the productions.
Programming languages often use restricted grammars to get linear parsing: e.g., regular grammars, s-grammars, LL grammars, and LR grammars.
The aspects of programming languages that can be modeled by context-free grammars are called the the syntax.
Aspects such as type-checking are not context-free. Such issues are sometimes considered (incorrectly in your instructor’s view) as part of the semantics of the language.
These are really still syntax, but they must be expressed in ways that are not context free.
Finite automata cannot recognize all context-free languages.
To recognize language , an automaton must do more than just verify that all ’s precede all ’s; it must count an unbounded number of symbols. This cannot be done with the finite memory of a dfa.
To recognize language , an automaton must do more than just count; it must remember a sequence of symbols and compare it to another sequence in reverse order.
Thus, to recognize context-free languages, an automaton needs unbounded memory. The second example above suggests using a stack as the “memory”.
Hence, the class of machines we examine in this chapter are called pushdown automata.
In this chapter, we examine the connection between context-free languages and pushdown automata.
Unlike the situation with finite automata, deterministic and nondeterministic pushdown automata differ in the languages they accept.
Nondeterministic pushdown automata (npda) accept precisely the class of context-free languages.
Deterministic pushdown automata (dpda) just accept a subset–the deterministic context-free languages.
Linz Figure 7.1 illustrates a pushdown automaton.
On each move, the control unit reads a symbol from the input file. Based on the input symbol and symbol on the top of the stack, the control unit changes the content of the stack and enters a new state.
Linz Definition 7.1 (Nondeterministic Pushdown Accepter): A nondeterministic pushdown accepter (npda) is defined by the tuple
,
where
is a finite set of internal states of the control unit,
is the input alphabet,
is a finite set of symbols called the stack alphabet,
finite subsets of is the transition function,
is the initial state of the control unit,
is the stack start symbol,
is the set of final states.
Note that the input and stack alphabets may differ and that start stack symbol must be an element of
Consider the transition function finite subsets of .
1st argument from is the current state.
2nd argument from is either the next input symbol or for a move that does not consume input.
3rd argument from is the current symbol at top of stack. (The stack cannot be empty! The stack start symbol represents the empty stack.)
The result value is a finite set of pairs where
The machine is nondeterministic, but there are only a finite number of possible results for each step.
Suppose the set of transition rules of an npda contains
.
A possible change in the state of the automaton from to is shown in following diagram.
This transition function can be drawn as a transition graph as shown below. The triple marking the edges represent the input symbol, the symbol at the top of the stack, and the string pushed back on the stack. Here we use “/” to separate the elements of the triple on the edges; the book uses commas for this purpose.
Consider an npda where
with the initial state and the transition function defined as follows:
There are no transitions defined for final state or in the cases
, , , , , and .
These are dead configurations of the npda. In these cases, maps the arguments to the empty set.
The machine executes as follows.
Acceptance or rejection?
The machine accepts:
Other strings will always end in dead configurations. For example:
Thus, it is not difficult to see that .
Linz Figure 7.2 shows a transition graph for this npda. The triples marking the edges represent the input symbol, the symbol at the top of the stack, and the string pushed back on the stack.
Transition graphs are useful for describing pushdown automata, but they are not useful in formal reasoning about them. For that, we use a concise notation for describing configurations of pushdown automata with tuples.
The triple where
is the control unit state
is the unread input string
is the stack as string, beginning with the top symbol
is called an instantaneous description of a pushdown automaton.
We introduce the symbol to denote a move from one instantaneous description to another such that
is possible if and only if
.
We also introduce the notation to denote an arbitrary number of steps of the machine.
Linz Definition 7.2 (Language Accepted by a Pushdown Automaton): Let be a nondeterministic pushdown automaton. The language accepted by is the set
.
In words, the language accepted by is the set of all strings that can put into a final state at the end of the string. The stack content is irrelevant to this definition of acceptance.
Construct an npda for the language
.
We must count ’s and ’s, but their relative order is not important.
The basic idea is:
But, what if at some point?
We must allow a “negative” count. So we modify the above solution as follows:
on “”,
on “”,
So the solution is an npda.
, with given as
Linz Figure 7.3 shows a transition graph for this npda.
In processing the string , the npda makes the following moves (as indicated by transition rule number):
(3) | ||
(6) | ||
(2) | ||
(5) | ||
(1) |
Hence, the string is accepted.
Construct an npda for accepting the language ,
The basic idea is:
push symbols from on stack from left to right
pop symbols from stack for (which is right-to-left)
Problem: How do we find the middle?
Solution: Use nondeterminism!
Each symbol could be at middle.
Automaton “guesses” when to switch.
For , a solution to the problem is given by , where:
– which is plus the staack start symbol
The transition function can be visualized as having several parts.
a set of transitions to push on the stack (one for each element of ):
a set of transitions to guess the middle of the string, where the npda switches from state to (any position is potentially the middle):
a set of transitions to match against the contents of the stack:
a transition to recognize a successful match:
Remember that, to be accepted, a final state must be reached with no unprocessed input remaining.
The sequence of moves accepting is as follows, where the number in parenthesis gives the transition rule applied:
(5) | ||
(2) | ||
(8) | ||
(10) | ||
(9) | ||
(11) |
Underlying idea: Given a context-free language, construct an npda that simulates a leftmost derivation for any string in the language.
We assume the context-free language is represented as grammar in Greibach Normal Form, as defined in Linz Chapter 6. We did not cover that chapter, but the definition and key theorem are shown below.
Greibach Normal Form restricts the positions at which terminals and variables can appear in a grammar’s productions.
Linz Definition 6.5 (Greibach Normal Form): A context-free grammar is said to be in Greibach Normal Form if all productions have the form
,
where and .
The structure of a grammar in Greibach Normal Form is similar to that of an s-grammar except that, unlike s-grammars, the grammar does not restrict pairs to single occurrences within the set of productions.
Linz Theorem 6.7 (Existence of Greibach Normal Form Grammars): For every context-free grammar with , there exists an equivalent grammar in Greibach normal form.
Underlying idea, continued: Consider a sentential form, for example,
where are the terminals read from the input and are the variables on the stack.
Consider a production .
If variable is on top of stack and terminal is the next input symbol, then remove from the stack and push back .
An npda transition function for must be defined to have the move
for some state , input string suffix , and stack . This, we define such that
.
Construct a pda to accept the language generated by grammar with productions
.
First, we transform this grammar into Greibach Normal Form:
We define the pda to have three states – an initial state , a final state , and an intermediate state .
We define the initial transition rule to push the start symbol on the stack:
We simulate the production with a transition that reads from the input and replaces on the top of the stack by .
Similarly, we simulate the production with a transition that reads while simply removing from the top of the stack. We represent these two productions in the pda as the nondeterministic transition rule:
Doing the same for the other productions, we get transition rules:
When the stack start symbol appears at the top of the stack, the derivation is complete. We define a transition rule to move the pda into its final state:
Linz Theorem 7.1 (Existence of NPDA for Context-Free Language): For any context-free language , there exists an npda such that .
Proof: The proof partly follows from the following construction (algorithm).
Algorithm to construct an npda for a context-free grammar
Let be a grammar for in Greibach Normal Form.
Construct npda where:
Define transition rule to initialize the stack.
For every in , define transition rules
that read , pop , and push . (Note the possible nondeterminism.)
Define transition rule to detect the end of processing.
Consider the grammar:
This grammar is already in Greibach Normal Form. So we can apply the algorithm above directly.
In addition to the transition rules for the startup and shutdown, i.e.,
the npda has the following transition rules for the productions:
The sequence of moves made by in processing is is
(1) | ||
(3) | ||
(4a) | ||
(4b) | ||
(6) | ||
(7) | ||
(2) |
This corresponds to the derivation
The previous construction assumed Greibach Normal Form. This is not necessary, but the needed construction technique is more complex, as sketched below.
e.g.,
etc.
Linz Theorem 7.2 (Existence of a Context-Free Language for an NPDA): If for some npda , then is a context-free language.
Basic idea: To construct a context-free grammar from an npda, reverse the previous construction.
That is, construct a grammar to simulate npda moves:
The stack content becomes the variable part of the grammar.
The processed input becomes the terminal prefix of sentential form.
This leads to a relatively complicated construction. This is described in the Linz textbook in more detail, but we will not cover it in this course.
A deterministic pushdown accepter (dpda) is a pushdown automaton that never has a choice in its move.
Linz Definition 7.3 (Deterministic Pushdown Automaton): A pushdown automaton is deterministic if it is an automaton as defined in Linz Definition 7.1, subject to the restrictions that, for every , and ,
contains at most one element,
if is not empty, then must be empty for every .
Restriction 1 requires that for, any given input symbol and any stack top, at most one move can be made.
Restriction 2 requires that, when a -move is possible for some configuration, no input-consuming alternative is available.
Consider the difference between this dpda definition and the dfa definition:
A dpda allows -moves, but the moves are deterministic.
A dpda may have dead configurations.
Linz Definition 7.4 (Deterministic Context-Free Language): A language is a deterministic context-free language if and only if there exists a dpda such that .
The language is a deterministic context-free language.
The pda with transition rules
accepts the given language. This grammar satisfies the conditions of Linz Definition 7.4. Therefore, it is deterministic.
Consider language
and machine
where:
The transition function can be visualized as having several parts:
Restriction 2 violation
Restriction 2 violation
This machines violates Restriciton 2 of Linz Definition 7.3 (Deterministic Pushdown Automaton) as indicated above. Thus, it is not deterministic.
Moreover, is itself not deterministic (which is not proven here).
Deterministic context-free languages are important because they can be parsed efficiently.
The dpda essentially defines a parsing machine.
Because it is deterministic, there is no backtracking involved.
We can thus easily write a reasonably efficient computer program to implement the parser.
Thus deterministic context-free languages are important in the theory and design of compilers for programming languages.
An LL-grammar is a generalization of the concept of s-grammar. This family of grammars generates the deterministic context-free languages.
Compilers for practical programming languages may use top-down parsers based on LL-grammars to parse the languages efficiently.
Chapter 4 examines the closure properties of the family of regular languages, algorithms for determining various properties of regular languages, and methods for proving languages are not regular (e.g., the Pumping Lemma).
Chapter 8 examines similar aspects of the family of context-free languages.
Because of insufficient time and extensive coverage of the Pumping Lemma for regular languages, we will not cover the Pumping Lemmas for Context-Free Languages in this course. See section 8.1 of the Linz textbook if you are interested in this topic.
Linz Section 8.1 includes the following language examples. The
results of these are used in the remainder of this chapter.
Linz Example 8.1 shows is not context free.
Linz Example 8.2 shows is not context free.
Linz Example 8.3 shows is not context free.
Linz Example 8.4 shows is not context free.
Linz Section 8.1 includes the following definitions. (The definition of linear grammar is actually from Chapter 3.)
Definition (Linear Grammar): A linear grammar is a grammar in which at most one variable can appear on the right side of any production.
A linear context-free grammar is thus a context-free grammar that is also a linear grammar.
Linz Definition 8.5 (Linear Language): A context-free language is linear if there exists a linear context-free grammar such that .
Linz Section 8.1 also includes the following language examples.
Linz Example 8.5 shows is a linear language.
Linz Example 8.6 shows is not linear.
In most cases, the proofs and algorithms for the properties of regular languages rely upon manipulation of transition graphs for finite automata. Hence, they are relatively straightforward.
When we consider similar properties for context-free languages, we encounter more difficulties.
Let’s consider closure under the simple set operations as we did for regular languages in Linz Theorem 4.1.
Linz Theorem 8.3 (Closure under Union, Concatenation, and Star-Closure): The family of context-free languages is closed under (a) union, (b) concatenation, and (c) star-closure.
(8.3a) Proof of Closure under Union:
Let and be context-free languages with the corresponding context-free grammars and .
Assume and are disjoint. (If not, we can make them so by renaming.)
Consider where
with:
– i.e, is a fresh variable
Clearly, is a context-free grammar. So is a context-free language.
Now, we need to show that .
For , there is a derivation in :
Similarly, for , there is a derivation in :
Also, for , the first step of the derivation must be either (1) or (2) .
For choice 1, the sentential forms derived from only have variables from . But is disjoint from . Thus the derivation
can only involve productions from from . Hence, for choice 1, .
Using a similar argument for choice 2, we conclude .
Therefore, .
QED.
(8.3b) Proof of Closure under Concatenation:
Consider where
with:
Then follows from a similar argument to the one in part (a).
QED.
(8.3c) Proof of Closure under Star-Closure:
Consider where
with:
Then follows from a similar argument to the one in part (a).
QED.
Linz Theorem 8.4 (Non-closure under Intersection and Complementation): The family of context-free languages is not closed under (a) intersection and (b) complementation.
(8.4b) Proof of Non-closure under Intersection:
Assume the family of context-free languages is closed under intersection. Show that this leads to a contradiction.
It is sufficient to find two context-free languages whose intersection is not context-free.
Consider languages and defined as follows:
One way to show that a language is context-free is to find a context-free grammar that generates it. The following context-free grammar generates :
Alternatively, we could observe that is the concatenation of two context-free languages and, hence, context-free by Linz Theorem 8.3 above.
Similarly, we can show that is context free.
From the assumption, we thus have that is context free.
But
,
which is not context free. Linz proves this in Linz Example 8.1 (which is in the part of this chapter we did not cover in this course).
Thus we have a contradiction. Therefore, the family of context-free languages is not closed under intersection.
QED.
(8.4b) Proof of Non-closure under Complementation:
Assume the family of context-free languages is closed under complementation. Show that this leads to a contradiction.
Consider arbitrary context-free languages and .
From set theory, we know that
.
From Linz Theorem 8.3 and the assumption that context-free languages are closed under complementation, we deduce that the right side () is a context-free language for all and .
However, we know from part (a) that the left side () is not necessarily a context-free language for all and .
Thus we have a contradiction. Therefore, the family of context-free languages is not closed under complementation.
QED.
Although context-free languages are not, in general, closed under intersection, there is a useful special case that is closed.
Linz Theorem 8.5 (Closure Under Regular Intersection): Let be a context-free language and be a regular language. Then is context free.
Proof:
Let be an npda that accepts context-free language .
Let be a dfa that accepts regular language .
We construct an npda
that simulates and operating simultaneously (i.e., executes the moves of both machines for each input symbol).
We choose pairs of states from and to represent the states of as follows:
We specify such that the moves of correspond to simultaneous moves of and . That is,
if and only if
and
.
For moves in , we extend so that for all .
By induction on the length of the derivations, we can prove that
,
with and if and only if
and
.
Therefore, a string is accepted by if and only if it is accepted by both and . That is, the string is in .
QED.
Show that the language
is context free.
We can construct an npda or context-free grammar for , but this is tedious. Instead, we use closure of regular intersection (Linz Theorem 8.5).
Let .
is finite, and thus also regular. Hence, is regular because regular languages are closed under complementation.
From previous results, we know that is context free.
Clearly, .
By the closure of context-free languages under regular intersection, is a context-free language.
Show that
is not context free.
Although we could use the Pumping Lemma for Context-Free Languages, we again use closure of regular intersection (Linz Theorem 8.5).
Assume that is context free. Show that this leads to a contradiction.
Thus
is also context free. But we have previously proved that this language is not context free.
Thus we have a contradiction. Therefore, is not context free.
There exist algorithms for determine whether a context-free language is empty or nonempty and finite or infinite.
These algorithms process the context-free grammars for the languages. They assume that the grammars are first transformed using various algorithms from Linz Chapter 6 (which we did not cover in this course).
The algorithms from Chapter 6 include the removal of
useless symbols and productions (i.e., variables and productions that can never generate a sentence)
-productions (i.e., productions with on the right side)
unit productions (i.e., productions of the form )
Linz Theorem 8.6 (Determining Empty Context-Free Languages): Given a context-free grammar , then there exists an algorithm for determining whether or not is empty.
Basic idea of algorithm: Assuming , remove the useless productions. If the start symbol is useless, then is empty. Otherwise, is nonempty.
Linz Theorem 8.7 (Determining Infinite Context-Free Languages): Given a context-free grammar , then there exists an algorithm for determining whether or not is infinite.
Basic idea of algorithm: Remove useless symbols, -productions, and unit productions. If there are variables that repeat as in
then the language is infinite. Otherwise, the language is finite. To determine repeated variables, we can build a graph of the dependencies of the variables on each other. If this graph has a cycle, then the variable at the base of the cycle is repeated.
Unfortunately, other simple properties are not as easy as the above.
For example, there is no algorithm to determine whether two context-free grammars generate the same language.
A finite accepter (nfa, dfa)
A pushdown accepter (npda, dpda)
has a stack for local storage
accepts a language from a larger family
The family of regular languages is a subset of the deterministic context-free languages, which is a subset of the context-free languages.
But, as we saw in Chapter 8, not all languages of interest are context-free. To accept languages like and , we need an automaton with a more flexible internal storage mechanism.
What kind of internal storage is needed to allow the machine to accept languages such as these? multiple stacks? a queue? some other mechanism?
More ambitiously, what is the most powerful automaton we can define? What are the limits of mechanical computation?
This chapter introduces the Turing machine to explore these theoretical questions. The Turing machine is a fundamental concept in the theoretical study of computation.
The Turing machine
Although Turing machines are simple mechanisms, the Turing thesis (also known as the Church-Turing thesis) maintains that any computation that can be carried out on present-day computers an be done on a Turing machine.
Note: Much of the work on computability was published in the 1930’s, before the advent of electronic computers a decade later. It included work by Austrian (and later American) logician Kurt Goedel on primitive recursive function theory, American mathematician Alonso Church on lambda calculus (a foundation of functional programming), British mathematician Alan Turing (also later a PhD student of Church’s) on Turing machines, and American mathematician Emil Post on Post machines.
Linz Figure 9.1 shows a schematic drawing of a standard Turing machine.
This deviates from the general scheme given in Chapter 1 in that the input file, internal storage, and output mechanism are all represented by a single mechanism, the tape. The input is on the tape at initiation and the output is on that tape at termination.
On each move, the tape’s read-write head reads a symbol from the current tape cell, writes a symbol back to that cell, and moves one cell to the left or right.
Turing machines were first defined by British mathematician Alan Turing in 1937, while he was a graduate student at Cambridge University.
Linz Definition 9.1 (Turing Machine): A Turing machine is defined by
where
We also require
and define
Requirement 8 means that the blank symbol cannot be either an input or an output of a Turing machine. It is the default content for any cell that has no meaningful content.
From requirement 9, we see that the arguments of the transition function are:
The result of the transition function gives:
In general, is a partial function. That is, not all configurations have a next move defined.
Consider a Turing machine with a move defined as follows:
Linz Figure 9.2 shows the situation (a) before the move and (b) after the move.
A Turing machine is a simple computer. It has
The Turing machine can
The transition function determines the behavior of the machine, i.e., it is the machine’s program.
The Turing macine starts in initial state and then goes through a sequence of moves defined by . A cell on the tape may be read and written many times.
Eventually the Turing machine may enter a configuration for which is undefined. When it enters such a state, the machine halts. Hence, this state is called a halt state.
Typically, no transitions are defined on any final state.
Consider the Turing machine defined by
,
,
,
where is defined as follows:
Linz Figure 9 .3 shows a sequence of moves for this Turing machine:
As with finite and pushdown automata, we can use transition graphs to represent Turing machines. We label the edges of the graph with a triple giving (1) the current tape symbol, (2) the symbol that replaces it, and (3) the direction in which the read-write head moves.
Linz Figure 9.4 shows a transition graph for the Turing machine given in Linz Example 9.2.
Consider the Turing machine defined in Linz Figure 9.5.
Suppose the tape initially contains with the read-write head positioned over the and in state . Then the Turing machine executes the following sequence of moves:
The machine reads symbol , leaves it unchanged, moves right (now over symbol ), and enters state .
The machine reads , leaves it unchanged, moves back left (now over again), and enters state again.
The machine then repeats steps 1-3.
Clearly, regardless of the tape configuration, this machine does not halt. It goes into an infinite loop.
Because we can define a Turing machine in several different ways, it is useful to summarize the main features of our model.
A standard Turing machine:
has a tape that is unbounded in both directions, allowing any number of left and right moves
is deterministic in that defines at most one move for each configuration
has no special input or output files. At the initial time, the tape has some specified content, some of which is considered input. Whenever the machine halts, some or all of the contents of the tape is considered output.
These definitions are chosen for convenience in this chapter. Chapter 10 (which we do not cover in this course) examines alternative versions of the Turing machine concept.
As with pushdown automata, we use instantaneous descriptions to examine the configurations in a sequence of moves. The notation (using strings)
or (using individual symbols)
gives the instantaneous description of a Turing machine in state with the tape as shown in Linz Figure 9.5.
By convention, the read-write head is positioned over the symbol to the right of the state (i.e., above).
A tape cell contains if not otherwise defined to have a value.
Example: The diagrams in Linz Figure 9.3 (above) show the instantaneous descriptions , , , and .
As with pushdown automata, we use to denote a move.
Thus, for transition rule
we can have the move
.
As usual we denote the transitive closure of move (i.e., arbitrary number of moves) using:
We also use subscripts to distinguish among machines:
Now let’s summarize the above discussion with the following definitions.
Linz Definition 9.2 (Computation): Let be a Turing machine. Then any string with and , is an instantaneous description of .
A move
is possible if and only if
.
A move
is possible if and only if
.
halts starting from some initial configuration if
for any and , for which is undefined.
The sequence of configurations leading to a halt state is a computation.
If a Turing machine does not halt, we use the following special notation to describe its computation:
Can a Turing machine accept a string ?
Yes, using the following setup:
Linz Definition 9.3 (Language Accepted by Turing Machine): Let be a Turing machine. Then the language accepted by is
.
Note: The finite string must be written to the tape with blanks on both sides. No blanks can are embedded within the input string itself.
Question: What if ?
The Turing machine might:
Any string for which the machine does not halt is, by definition, not in .
For , design a Turing machine that accepts the language denoted by the regular expression .
We use two internal states , one final state , and transition function:
,
The transition graph shown below implements this machine.
The Turing machine also halts in a final state if started in state on a blank. We could interpret this as acceptance of , but for technical reasons the empty string is not included in Linz Definition 9.3.
For , design a Turing machine that accepts
.
We can design a machine that incorporates the following algorithm:
Filling in the details, we get the following Turing machine for which:
The transitions can be broken into several sets.
The first set
replaces the leftmost with an , then causes the read-write head to travel right to the first , replacing it with a . The machine then enters a state , indicating that an has been successfully paired with a .
The second set
reverses the direction of movement until an is encountered, repositions the read-write head over the leftmost , and returns control to the initial state.
The machine is now back in the initial state , ready to process the next - pair.
After one pass through this part of the computation, the machine has executed the partial computation:
So, it has matched a single with a single .
The machine continues this process until no is found on leftward movement.
If all ’s have been replaced, then state detects a instead of an and changes to state . This state must verify that all ’s have been processed as well.
The input makes the moves shown below. (The bold number in parenthesis gives the rule applied in that step.)
– start at left end | |||
(1) | – process 1st a-b pair | ||
(2) | – moving to right | ||
(4) | |||
(6) | – move back to left | ||
(7) | |||
(1) | – process 2nd a-b pair | ||
(3) | – moving to right | ||
(4) | |||
(5) | – move back to left | ||
(7) | |||
(8) | – no a’s | ||
(9) | – check for extra b’s | ||
(10) | – done, move to final |
The Turing machine halts in final state , thus accepting the string .
If the input is not in the language, the Turing machine will halt in a nonfinal state.
For example, consider:
Turing machines are more than just language accepters. They provide a simple abstract model for computers in general. Computers transform data. Hence, Turing machines are transducers (as we defined them in Chapter 1). For a computation, the
input consists of all the nonblank symbols on the tape initially
output consists of is whatever is on the tape when the machine halts in a final state
Thus, we can view a Turing machine transducer as an implementation of a function defined by
provided that
,
for some final state .
Linz Definition 9.4 (Turing Computable): A function with domain is said to be Turing-computable, or just computable, if there exists some Turing machine such that
, ,
for all .
Note: A transducer Turing machine must start on the leftmost symbol of the input and stop on the leftmost symbol of the output.
Compute for positive integers and .
We use unary notation to represent the positive integers, i.e., a positive integer is represented by a sequence of 1’s whose length is equal to the value of the integer. For example:
The computation is
where separates the two numbers at initiation and after the result at termination.
Key idea: Move the separating to the right end.
To achieve this, we construct with
The sequence of instantaneous descriptions for adding 111 to 11 is shown below.
Construct a Turing machine that copies strings of ’s. More precisely, find a machine that performs the computation
,
for any .
To solve the problem, we implement the following procedure:
A Turing machine transition function for this procedure is as follows:
where is the only final state.
Linz Figure 9.7 shows a transition graph for this Turing machine.
This is not easy to follow, so let us trace the program with the string 11. The computation performed is as shown below.
Suppose and are positive integers represented in unary notation.
Construct a Turing machine that halts in a final state if and in a nonfinal state if .
That is, the machine must perform the computation:
, if
, if
We can adapt the approach from Linz Example 9.7. Instead of matching ’s and ’s, we match each 1 on the left of the dividing 0 with the 1 on the right. At the end of the matching, we will have on the tape either
or
,
depending on whether or .
A transition graph for machine is shown below.
How can we compose simpler operations on Turing machines to form more complex operations?
Techniques discussed in this section include use of:
Top-down stepwise refinement, i.e., starting with a high-level description and refining it incrementally until we obtain a description in the actual language
Block diagrams or pseudocode to state high-level descriptions
In the block diagram technique, we define high-level computations in boxes without internal details on how computation is done. The details are filled in on a subsequent refinement.
To explore the use of block diagrams in the design of complex computations, consider Linz Example 9.12, which builds on Linz Examples 9.9 and 9.11 (above).
Design a Turing machine that computes the following function:
, if ,
, if .
For simplicity, we assume and are positive integers in unary representation and the value zero is represented by 0, with the rest of the tape blank.
Linz Figure 9.8 shows a high-level block diagram of this computation. This computation consists of a network of three simpler machines:
We use such high-level diagrams in subsequent discussions of large computations. How can we justify that practice?
We can implement:
the Comparer program as suggested in Linz Example 9.11, using a Turing machine having states indexed with
the Adder program as suggested in Linz Example 9.9, with states indexed with
the Eraser program by constructing a Turing machine having states indexed with
Comparer carries out the computations
, if ,
and
, if .
If and are the initial states of computations and , respectively, then starts either or .
Adder carries out the computation
.
And, Eraser carries out the computation
.
The outer diagram in Linz Figure 9.8 thus represents a single Turing machine that combines the actions of machines , , and as shown.
In the pseudocode technique, we outline a computation using high-level descriptive phrases understandable to people. We refine and translate it to lower-level implementations later.
A simple kind of pseudocode is the macroinstruction. A macroinstruction is a single statement shorthand for a sequence of lower-level statements.
We first define the macroinstructions in terms of the lower-level language. Then we compose macroinstructions into a larger program, assuming the relevant substitutions will be done.
For this example, consider the macroinstruction
if then else .
This means:
If the Turing machine reads an , then it, regardless of its current state, transitions into state without changing the tape content or moving the read-write head.
If the symbol read is not an , then it transitions into state without changing anything.
We can implement this macroinstruction with several steps of a Turing machine:
for all
for all
for all and all
for all
States and just back up Turing machine tape position one place.
Macroinstructions are expanded at each occurrence.
While each occurrence of a macroinstruction is expanded into actual code, a subprogram is a single piece of code that is invoked repeatedly.
As in higher-level language programs, we must be able to call a subprogram and then, after execution, return to the calling point and resume execution without any unwanted effects.
How can we do this with Turing machines?
We must be able to:
preserve information about the calling program’s configuration (state, read-write head position, tape contents), so that it can be restored on return from the subprogram
pass information from the calling program to the called subprogram and vice versa
We can do this by partitioning the tape into several regions. Linz Figure 9.9 illustrates this technique for a program (a Turing machine) that calls a subprogram (another Turing machine).
Note: This is similar to what happens in an actual computer for a subprogram (function, procedure) call. The region is normally a segment pushed onto the program’s runtime stack or dynamically allocated from the heap memory.
Design a Turing machine that multiplies and , positive integers represented in unary notation.
Assume the initial and final tape configurations are as shown in Linz Figure 9.10.
We can multiply by by adding to itself times as described in the algorithm below.
Although the above description of the pseudocode approach is imprecise, the idea is sufficiently simple that it is clear we can implement it.
We have not proved that the block diagram, macroinstruction, or subprogram approaches will work in all cases. But the discussion in this section shows that it is plausible to use Turing machines to express complex computations.
The Turing thesis is an hypothesis that any computation that can be carried out by mechanical means can be performed by some Turing machine.
This is a broad assertion. It is not something we can prove!
The Turing thesis is actually a definition of mechanical computation: a computation is mechanical if and only if it can be performed by some Turing machine.
Some arguments for accepting the Turing thesis as the definition of mechanical computation include:
Anything that can be computed by any existing digital computer can also be computed by a Turing machine.
There are no known problems that are solvable by what we intuitively consider an algorithm for which a Turing machine program cannot be written.
No alternative model for mechanical computation is more powerful than the Turing machine model.
The Turing thesis is to computing science as, for example, classical Newtonian mechanics is to physics. Newton’s “laws” of motion cannot be proved, but they could possibly be invalidated by observation. The “laws” are plausible models that have enabled humans to explain much of the physical world for several centuries.
Similarly, we accept the Turing thesis as a basic “law” of computing science. The conclusions we draw from it agree with what we know about real computers.
The Turing thesis enables us to formalize the concept of algorithm.
Linz Definition 9.5 (Algorithm): An algorithm for a function is a Turing machine , which given as input any on its tape, eventually halts with the correct answer on its tape. Specifically, we can require that
,
for all .
To prove that “there exists an algorithm”, we can construct a Turing machine that computes the result.
However, this is difficult in practice for such a low-level machine.
An alternative is, first, to appeal to the Turing thesis, arguing that anything that we can compute with a digital computer we can compute with a Turing machine. Thus a program in suitable high-level language or precise pseudocode can compute the result. If unsure, then we can validate this by actually implementing the computation on a computer.
Note: A higher-level language is Turing-complete if it can express any algorithm that can be expressed with a Turing machine. If we can write a Turing machine simulator in that language, we consider the language Turing complete.
The kinds of questions addressed in this chapter:
What is the family of languages accepted by Turing machines?
Are there any languages that are not accepted by any Turing machine?
What is the relationship between Turing machines and various kinds of grammars?
How can we classify the various families of languages and their relationships to one another?
Note: We assume the languages in this chapter are -free unless otherwise stated.
Here we make a distinction between languages accepted by Turing machines and languages for which there is a membership algorithm.
Definition (Countable and Countably Infinite): A set is countable if it has the same cardinality as a subset of the natural numbers. A set is countably infinite if it can be placed into one-to-one correspondence with the set of all natural numbers.
Thus there is some ordering on any countable set.
Also note that, for any finite set of symbols , then and any its subsets are countable. Similarly for .
From Linz Section 10.4 (not covered in this course), we also have the following theorem about the set of Turing machines.
Linz Theorem 10.3 (Turing Machines are Countable): The set of all Turing machines is countably infinite.
Linz Definition 11.1 (Recursively Enumerable Language): A language is recursively enumerable if there exists a Turing machine that accepts it.
This definition implies there is a Turing machine such that for every
with the initial state and a final state .
But what if ?
Linz Definition 11.2 (Recursive Language): A language on is recursive if there exists a Turing machine that accepts and that halts on every in .
That is, a language is recursive if and only if there exists a membership algorithm for it.
If a language is recursive, then there exists an enumeration procedure, that is, a method for counting and ordering the strings in the language.
Let be a Turing machine that determines membership in a recursive language on an alphabet .
Let be modified to write the accepted strings to its tape.
is countable, so there is some ordering of . Construct Turing machine that generates all in order, say .
Thus generates the candidate strings in order. writes the the accepted strings to its tape in order.
Problem: A Turing machine might not halt on some strings.
Solution: Construct to advance “all” strings simultaneously, one move at a time. The order of string generation and moves is illustrated in Linz Figure 11.1.
Now machine advances each candidate string (columns of Linz Figure 11.1) one -move at a time.
Because each string is generated by and accepted by in a finite number of steps, every string in is eventually produced by . The machine does not go into an infinite loop for a that is not accepted.
Note: Turing machine does not terminate and strings for which does not halt will never complete processing, but any string that can be accepted by will be accepted within a finite number of steps.
Linz Theorem 11.1 (Powerset of Countable Set not Countable) Let be an countably infinite set. Then its powerset is not countable.
Proof: Let be an countably infinite set.
Let . Then can represented by a bit vector such that if and only if .
Assume is countable. Thus can be written in order and put into a table as shown in Linz Figure 11.2.
Consider the main diagonal of the table (circled in Linz Figure 11.2). Complement the bits along this diagonal and let be a set represented by this bit vector.
Clearly . But for any , because they differ at least at . This is a contradicts the assumption that is countable.
So the assumption is false. Therefore, is not countable. QED.
This is Cantor’s diagonalization argument.
Linz Theorem 11.2 (Existence of Languages Not Recursively Enumerable): For any nonempty , there exist languages that are not recursively enumerable.
Proof: Any is a language on . Thus is the set of all languages on .
Because is infinite and countable, Linz Theorem 11.1 implies that the set of all languages on is not countable. From Linz Theorem 10.3 (see above), we know the set of Turing machines can be enumerated. Hence, the recursively enumerable languages are countable.
Therefore, some languages on are not recursively enumerable. QED.
Linz Theorem 11.3: There exists a recursively enumerable language whose complement is not recursively enumerable.
Proof: Let .
Consider the set of all Turing machines with input alphabet , i.e., .
By Linz Theorem 10.3 (see above), we know that this set of is countable. So it has some order.
For each there exists a recursively enumerable language .
Also, for each recursively enumerable languages on , there is some Turing machine that accepts it.
Let .
is recursively enumerable because here is a Turing machine that accepts it. E.g., the Turing machine works as follows:
Now consider .
Assume is recursively enumerable.
There must be some Turing machine , for some , that accepts . Hence, .
Consider . Is it in ? Or in ?
Consider the case . Thus . Hence, by the definition of . This is a contradiction.
Consider the case , i.e., . Thus by definition of . But from the defintion of , . This is also be a contradiction.
In all cases, we have a contradiction, so the assumption is false. Therefore, is not recursively enumerable. QED.
Linz Theorem 11.4: If a language and its complement are both recursively enumerable, then both languages are recursive. If is recursive, then is also recursive, and consequently both are recursively enumerable.
Proof: See Linz Section 11.2 for the details.
Linz Theorem 11.5: There exists a recursively enumerable language that is not recursive; that is, the family of recursive languages is a proper subset of the family of recursively enumerable languages.
Proof: Consider the language of Linz Theorem 11.3.
This language is recursively enumerable, but its complement is not. Therefore, by Linz Theorem 11.4, it is not recursive, giving us the required example. QED.
There are well-defined languages that have no membership algorithms.
Linz Definition 11.3 (Unrestricted Grammar): A grammar is an unrestricted gramar if all the productions are of the form
,
where is in and is in .
Note: There is no on left, but otherwise the use of symbols is unrestricted.
Linz Theorem 11.6 (Recursively Enumerable Language for Unrestricted Grammar): Any language generated by an unrestricted grammar is recursively enumerable.
Proof: See Linz Section 11.2 for the details.
The grammar defines an enumeration procedure for all strings.
Linz Theorem 11.7 (Unrestricted Grammars for Recursively Enumerable Language): For every recursively enumerable language , there exists an unrestricted grammar , such that .
Proof: See Linz Section 11.2 for the details.
Between the restricted context-free grammars and the unrestricted grammars, there are a number of kinds of “somewhat restricted” families of grammars.
Linz Definition 11.4 (Context-Sensitive Grammar): A grammar is said to be context-sensitive if all productions are of the form
,
where and
.
This type of grammar is noncontracting in that the length of successive sentential forms can never decrease.
All such grammars can be rewritten in a normal form in which all productions are of the form
.
This is equivalent to saying that the production
can only be applied in a context where occurs with string on the left and string on the right.
Linz Definition 11.5 (Context-Sensitive) : A language is said to be context-sensitive if there exists a context-sensitive grammar , such that or .
Note the special cases for . This enables us to say that the family of context-free languages is a subset of the family of context-sensitive languages.
The language is a context-sensitive language. We show this by defining a context-sensitive grammar for the language, such as the following:
Consider a derivation of :
The grammar uses the variables and as messengers.
An is created on the left, travels to the right to the first , where it creates another and .
Messanger is sent back to the left to create the corresponding .
The process is similar to how a Turing machine would work to accept the language .
is not context-free.
In Linz Section 10.5 (not covered in this course), a linear-bounded automaton is defined as a nondeterministic Turing machine that is restricted to the part of its tape occupied by its input (bounded on the left by and right by ).
.
Linz Theorem 11.8: For every context-sensitive language not including , there exists some linear bounded automaton such that :
Proof: See Linz Section 11.3 for the details.
Linz Theorem 11.9: If a language is accepted by some linear bounded automaton , then there exists a context-sensitive grammar that generates .
Proof: See Linz Section 11.3 for the details.
Linz Theorem 11.10: Every context-sensitive language is recursive.
Linz Theorem 11.11: There exists a recursive language that is not context-sensitive.
We have studied a number of automata in this course. Ordered by decreasing power these include:
We have studied a number of types of languages in this course, including
One way of showing the relationship among these families of languages is to use the Chomsky hierarchy, where the types are numbered as above and as diagrams in Linz Figures 11.3 and 11.4.
This classification was first described in 1956 by American linguist Noam Chomsky, a founder of formal language theory.
In Linz Chapter 9, we studied the Turing thesis, which concerned what Turing machines can do.
This chapter we study: What Turing machines cannot do.
This chapter considers the concepts:
Recall the following definition from Chapter 9.
Linz Definition 9.4 (Turing Computable): A function with domain is said to be Turing-computable, or just computable, if there exists some Turing machine such that
, ,
for all .
Note:
Here we work in a simplified setting: the result of a computation is either “yes” or “no”. In this context, the problem is considered either decidable or undecidable.
Problem: We have a set of related statements, each either true or false.
This problem is decidable if and only if there exists a Turing machine that gives the correct answer for every statement in the domain. Otherwise, the problem is undecidable.
Example problem statement: For a context-free grammar , the language is ambiguous. This is a true statement for some and false for others.
If we can answer this question, with either the result true or false, for every context-free grammar, then the problem is decidable. If we cannot answer the question for some context-free grammar (i.e., the Turing machine does not halt), then the problem is undecidable.
(In Linz Theorem 12.8, we see that this question is actually undecidable.)
Given the description of a Turing machine and input string , does , when started in the initial configuration , perform a computation that eventually halts?
What is the domain ?
We cannot solve this problem by simulating . That is an infinite computation if the Turing machine does not halt.
We must analyze the Turing machine description to get an answer for any machine and string . But no such algorithm exists!
Linz Definition 12.1 (Halting Problem): Let be a string that describes a Turing machine and let be a string in ’s alphabet. Assume that and are encoded as strings of 0’s and 1’s (as suggested in Linz Section 10.4). A solution to the halting problem is a Turing machine , which for any and , performs the computation
if is applied to halts, and
,
if is applied to does not halt. Here and are both final states of .
Linz Theorem 12.1 (Halting Problem is Undecidable): There does not exist any Turing machine that behaves as required by Linz Definition 12.1. Thus the halting problem is undecidable.
Proof: Assume there exists such a Turing machine that solves the halting problem.
The input to is , where is a description of Turing machine . must halt with a “yes” or “no” answer as indicated in Linz Figure 12.1.
We next modify to produce a Turing machine with the structure shown in Linz Figure 12.2.
When reaches a state where halts, it enters an infinite loop.
From we construct Turing machine , which takes an input and copies it, ending in initial state of . After that, it behaves the same as .
The behavior of is
if applied to halts, and
if applied to does not halt.
Now is itself a Turing machine, which can be also be encoded as a string .
So, let’s apply to its own description . The behavior is
if applied to halts, and
if applied to does not halt.
In the first case, goes into an infinite loop (i.e., does not halt) if halts. In the second case, halts if does not halt. This is clearly impossible!
Thus we have a contradiction. Therefore, there exists no Turing machine . The halting problem is undecidable. QED.
It may be possible to determine whether a Turing machine halts in specific cases by analyzing the machine and its input.
However, this theorem says that there exists no algorithm to solve the halting problem for all Turing machines and all possible inputs.
Linz Theorem 12.2: If the halting problem were decidable, then every recursively enumerated language would be recursive. Consequently, the halting problem is undecidable.
Proof: Let be a recursively enumerable language on , be a Turing machine that accepts , and be an encoding of as a string.
Assume the halting problem is decidable and let be a Turing machine that solves it.
Consider the following procedure.
The above is thus a membership algorithm, so must be recursive. But we know that there are recursively enumerable languages that are not recursive. So this is a contradiction.
Therefore, cannot exist and the halting problem is undecidable. QED.
In the above, the halting problem is reduced to a membership algorithm for recursively enumerable languages.
A problem is reduced to problem if the decidability of implies the decidability of . We transform a new problem into a problem whose decidability is already known.
Note: The Linz textbook gives three example reductions in Section 12.1
Linz Theorem 12.3 (Empty Unrestricted Grammars Undecidable): Let be an unrestricted grammar. Then the problem of determining whether or not
is undecidable.
Proof: See Linz Section 12.2 for the details of this reduction argument. The decidability of membership problem for recursively enumerated languages implies the problem in this theorem.
Linz Theorem 12.4 (Finiteness of Turing Machine Languages is Undecided): Let be a Turing Machine. Then the question of whether or not is finite is undecidable.
Proof: See Linz Section 12.2 for the details of this proof.
Rice’s theorem, a generalization of the above, states that any nontrivial property of a recursively enumerable language is undecidable. The adjective “nontrivial” refers to a property possessed by some but not all recursively enumerated languages.
This section is not covered in this course.
Linz Theorem 12.8: There exists no algorithm for deciding whether any given context-free grammar is ambiguous.
Proof: See Linz Section 12.4 for the details of this proof.
Linz Theorem 12.9: There exists no algorithm for deciding whether or not
for arbitrary context-free grammars and .
Proof: See Linz Section 12.4 for the details of this proof.
Keep in mind that the above and other such decidability results do not eliminate the possibility that there may be specific cases–perhaps even many interesting and important cases–for which there exist decision algorithms.
However, these theorems do say that there are no general algorithms to decide these problems. There are always some cases in which specific algorithms will fail to work.
This section is not covered in this course.