Lecture3_Computability
Lecture3_Computability
FACULTY OF ENGINEERING
COMPUTER ENGINEERING DEPARTMENT
Lecture 3: Computability
Repetition from last week: A TM can be formally described as a 7-tuple (Ʃ, T, S, δ, s0, #, F)
where
• Ʃ is the input alphabet
• T is the tape alphabet (wheras Ʃ ⊂ 𝐓)
• S is a finite set of states
• δ is a transition function; δ : S × T → S × T × {Left_shift, Right_shift, or
no_movement}.
• s0 is the initial state
• # is the blank symbol
• F is the set of final states
• Ʃ = {1, 0}
• T = {1, 0, #}
• S = {s0, sf }
• S0 = {s0}
• # = blank symbol
• F = {sf }
δ is given by
Current state Symbol read Symbol written New state Move instruction
s0 Blank (#) → None sf None
s0 0 → Write 1 s0 Move tape to the right
s0 1 → Write 0 s0 Move tape to the right
The automaton will first read the symbol under the head, write a new symbol accordingly, then
move the tape left or right as instructed, before repeating the read-write-move sequence again.
Let's see what this program does to our tape from the previous end point of the instructions:
The current symbol under the head is 0, so we write a 1 and move the tape right by one square.
The symbol being read is now 1, so we write a 0 and move the tape right by one square:
Finally, a 'blank' symbol is read, so the machine does nothing apart from read the blank symbol
continuously since we have instructed it to repeat the read-write-move sequence without
stopping.
After every instruction, we also specify a state for the machine to transition to. In the example,
the machine is redirected back to its original state, State 0, to repeat the read-write-move
sequence, unless a blank symbol is read. When the machine reads a blank symbol, the machine
is directed to a stop state and the program terminates.
QA is similar to Pushdown Automaton (PDA) but has a queue instead of a stack which helps
Queue automaton to recognize languages beyond Context Free Languages. QA works
according to First-in First-out (FIFO) principle.
The working tape is a queue, i.e. a data structure that only allows two types of access−
• enqueue − a new symbol is added at the rear of the queue.
• dequeue − the symbol at the front of the queue is removed.
Figure 2: Design of QA
(𝑠0 , #, 𝑠0 , ε),
(𝑠0 , 𝑎, 𝑠𝑎 , ε),
(𝑠𝑎 , 𝑎, 𝑠𝑎 , a),
(𝑠𝑎 , 𝑏, 𝑠𝑏 , ε),
=
(𝑠𝑏 , 𝑏, 𝑠𝑏 , b),
(𝑠𝑏 , 𝑐, 𝑠𝑐 , ε),
(𝑠𝑐 , 𝑐, 𝑠𝑐 , c),
{ (𝑠𝑐 , #, 𝑠0 , #) }
Input word: 𝑎2 𝑏2 𝑐 2
(𝑠0 , 𝑎2 𝑏2 𝑐 2 #) ⊢ (𝑠𝑎 , 𝑎𝑏2 𝑐 2 #) ⊢ (𝑠𝑎 , 𝑏2 𝑐 2 #𝑎) ⊢ (𝑠𝑏 , 𝑏𝑐 2 #𝑎) ⊢ (𝑠𝑏 , 𝑐 2 #𝑎𝑏) ⊢ (𝑠𝑐 , 𝑐#𝑎𝑏)
⊢ (𝑠𝑐 , #𝑎𝑏𝑐) ⊢ (𝑠0 , 𝑎𝑏𝑐#) ⊢ (𝑠𝑎 , 𝑏𝑐#) ⊢ (𝑠𝑏 , 𝑐#) ⊢ (𝑠𝑐 , #) ⊢ (𝑠0 , #) ⊢ (𝑠0 , ε)
Note: It has been proven that a queue machine is equivalent to a Turing machine by showing
that a queue machine can simulate a Turing machine and vice versa.
Furthermore, it has been proved that a PDA with 2 stacks (2PDA) is also equivalent to a Turing
machine.
Consequently (!):
In addition to Turing computability, two further computability terms are the Queue
computability and the 2PDA computability.
In this section we introduce further computability concepts. Unlike Turing machines, they are
not based on the formalization of an intuitive concept of computability, but on concepts of
procedural programming languages. We will show that goto and while computability are
equivalent to each other and to Turing computability, and that loop computability is weaker
than these.
We first define the syntax of the LOOP programming language, then we define the semantics
of LOOP programs.
Character strings with the above alphabet form syntactically correct LOOP statements if they
can only be generated with the following rules:
(i) The value assignments xi := xj + c and xi := xj - c are LOOP statements, where xi
and xj are variables, allowing i = j, and c is a constant identifier.
(ii) Let be A, A1 and A2 LOOP statements, then
a. A1; A2 and
b. loop x do A end;
is a LOOP program.
The programming language LOOP has no explicit selection statement, i.e., no statement of the
kind
where B is a condition and 𝐴1 and 𝐴2 are two statements. However, if statements can be
simulated by LOOP statements. For the instruction
𝑥𝑞 ≔ 1
𝑥𝑟 ≔ 1
loop 𝑥𝑝 do 𝑥𝑞 ≔ 0 end;
loop 𝑥𝑞 do 𝐴1 ; 𝑥𝑟 ≔ 0 end;
loop 𝑥𝑟 do 𝐴2 end ;
Note: Since the counter variable in the loop body cannot be changed, it is an essential property
of LOOP programs that they always terminate. It follows that the functions computed by LOOP
programs are total.
We add another loop statement to the LOOP programming language, the while statement, and
call the extended programming language WHILE. In contrast to the loop statement, the number
of executions of the loop body in the while statement is not fixed before the beginning of the
evaluation, but the number of executions can depend on the results of the loop body executions.
Accordingly, the loop variable in the loop body of the while statement may not only be used,
but also manipulated, i.e., it may also be "write" accessed.
while x ≠0 do A end;
while xi ≠0 do A endwhile
a WHILE program.
label x; A; goto x;
In addition to symbols for variables x0, x1, … and constants c0, c1, …, the alphabet of GOTO
programming language includes symbols for labels, which we denote by l1, l2, ... (l stands for
label), as well as the keywords read, write, goto, if, then, and stop, the assignment symbol :=,
the operation symbols =, +, -, and the separators : and ;. The following statements are unmarked
GOTO statements:
i. the assignments xi := xj + ck and xi := xi - ck,
ii. (unconditional) jumps goto li,
iii. conditional jumps if xi = ci then goto lk,
iv. the stop statement stop.
l:A
𝐫𝐞𝐚𝐝 (𝑥1 , … , 𝑥k );
𝑙1 : = 𝐴1 ;
.
.
.
𝑙n : = 𝐴n ;
write (𝑥0)
Relationship:
loop ⊂ while = goto = Turing computability
It is known that
f: loop-computable functions → total computable functions
Primitive Functions:
𝑓(𝑥1 , … , 𝑥𝑛 , 0) = 𝑔(𝑥1 , … , 𝑥𝑛 )
Definition:
𝑎𝑑𝑑(𝑥, 0) = 𝑥
Example:
𝑎𝑑𝑑(2,3) = 𝑆 (π33 (2,3, 𝑎𝑑𝑑(2,2)))
Let h1, …, hn: ℕ0 → ℕ0, g: ℕ𝑛0 → ℕ0 be primitive recursive, then f: ℕ𝑘0 → ℕ0 with
or
h1, …, hn: ℕ0 → ℕ0, g: ℕ𝑛0 → ℕ0 is a primitive rekursive function, then f: ℕ𝑘0 → ℕ0 with
Definition:
𝑚𝑢𝑙𝑡(𝑥, 0) = 0
Example:
𝑥 − 𝑦, 𝑥≥𝑦
𝑠𝑢𝑏: 𝑁0 𝑥 𝑁0 → 𝑁0 𝑠𝑢𝑏(𝑥, 𝑦) = {
0, 𝑥<𝑦
To show that the sub function is primitive recursive, we need a predecessor function 𝑃. The
predecessor function 𝑃 acts as the opposite of the successor function 𝑆 and is recursively
defined by the rules:
𝑥 − 1, 𝑥≥1
𝑃: 𝑁0 → 𝑁0 𝑃(𝑥) = {
0, 𝑜𝑡ℎ𝑒𝑟𝑤𝑖𝑠𝑒
Definition:
𝑃(0) = 0
𝑃(𝑆(𝑥)) = π12 (𝑥, 𝑃(𝑥))
Example:
Predecessor of 4?
With the help of the predecessor function 𝑃, now we can show that the sub function is primitive
recursive:
Definition:
𝑠𝑢𝑏(𝑥, 0) = 𝑥
𝑠𝑢𝑏(𝑥, 𝑆(𝑦)) = 𝑃 (π33 (𝑥, 𝑦, 𝑠𝑢𝑏(𝑥, 𝑦)))
Example:
𝑠𝑢𝑏(4,2) = 𝑃 (π33 (4,2, 𝑠𝑢𝑏(4,1)))
𝑒𝑥𝑝: 𝑁0 𝑥 𝑁0 → 𝑁0 𝑒𝑥𝑝(𝑏, 𝑦) = 𝑏 𝑦
Definition:
𝑒𝑥𝑝(𝑥, 0) = 1
𝑒𝑥𝑝(𝑥, 𝑆(𝑦)) = 𝑚𝑢𝑙𝑡 ∘ (π13 , π33 )(𝑥, 𝑦, 𝑒𝑥𝑝(𝑥, 𝑦))
Example:
𝑒𝑥𝑝(2,3) = 𝑚𝑢𝑙𝑡 ∘ (π13 , π33 )(2,3, 𝑒𝑥𝑝(2,2))
= 𝑚𝑢𝑙𝑡 ∘ (π13 , π33 ) (2,3, 𝑚𝑢𝑙𝑡 ∘ (π13 , π33 ) (2,3, 𝑚𝑢𝑙𝑡 ∘ (π13 , π33 )(2,3, 𝑒𝑥𝑝(2,0))))
= 𝑚𝑢𝑙𝑡 ∘ (π13 , π33 ) (2,3, 𝑚𝑢𝑙𝑡 ∘ (π13 , π33 )(2,3, 𝑚𝑢𝑙𝑡 ∘ (π13 , π33 )(2,3,1)))
µ-Operator (Mikro-Operator):
f: ℕ𝑛+1
0 → ℕ0 µ[f]: ℕ𝑛0 → ℕ0
Example:
Assumption: b is constant. The µ-recursive computation of 𝑙𝑜𝑔: ℕ20 → ℕ0 with the following
definition:
µ[log] (4, 0) = 3
µ[log] (4, 1) = 2
µ[log] (4, 2) = 0 The correct result (Note: The log function is not total)
A distinction is made between the set of µ-recursive functions that are total, i.e. ℝ.
Ackermann Function
We know that
f: loop-computable functions → total computable functions
𝑎𝑐𝑘(0, 𝑚) = 𝑚 + 1
𝑎𝑐𝑘(𝑛, 0) = 𝑎𝑐𝑘(𝑛 − 1,1)
𝑎𝑐𝑘 (𝑛, 𝑚) = 𝑎𝑐𝑘(𝑛 − 1, 𝑎𝑐𝑘 (𝑛, 𝑚 − 1))
Example #1:
Example #2:
= 𝑎𝑐𝑘 (0, 𝑎𝑐𝑘 (0, 𝑎𝑐𝑘 (0, 𝑎𝑐𝑘 (0, 𝑎𝑐𝑘(0, 𝑎𝑐𝑘(1,0))))))
= 𝑎𝑐𝑘 (0, 𝑎𝑐𝑘 (0, 𝑎𝑐𝑘 (0, 𝑎𝑐𝑘 (0, 𝑎𝑐𝑘(0, 𝑎𝑐𝑘(0,1))))))
The Ackermann function is a tremendously increasing function. For example, for all y:
𝑎𝑐𝑘(1, 𝑦) = 𝑦 + 2,
𝑎𝑐𝑘(2, 𝑦) = 2𝑦 + 3,
𝑎𝑐𝑘(3, 𝑦) = 2𝑦+3 − 3
…2
𝑎𝑐𝑘(4, 𝑦) = 22 −3
ack(4,0) = 16 − 3 = 13,
Ackermann's function, while Turing computable, grows faster than any primitive recursive
function. The reason can be seen from the table: the index for the next value is also growing.
𝑎𝑐𝑘(𝑚, 𝑛)
⇕
𝑎𝑐𝑘𝑚 (𝑛) → Family of Ackermann functions with m=0, m=1, m=2, etc.
The Ackermann function, due to its definition in terms of extremely deep recursion, can be used
as a benchmark of a compiler’s ability to optimize recursion. The first use of Ackermann's
function in this way was by Yngve Sundblad, The Ackermann function. A Theoretical,
computational and formula manipulative study.
• For example, a compiler which, in analyzing the computation of A(3, 30), is able to save
intermediate values like A(3, n) and A(2, n) in that calculation rather than recomputing
them, can speed up computation of A(3, 30) by a factor of hundreds of thousands.
• Also, if A(2, n) is computed directly rather than as a recursive expansion of the form
A(1, A(1, A(1,...A(1, 0)...))), this will save significant amounts of time.
Conclusion:
So far, we have seen five formal concepts for the notion of computability: Turing, loop, while
and goto computability as well as µ-recursive functions. Turing computability starts from an
intuitive understanding of computability, 'calculating with pencil and paper'. Loop, while, and
goto computability are programming language-like concepts, and the µ-recursive functions
describe computability using only mathematical functions.
Note that they are many other computability concepts available, including Universal Register
Machines (URM), λ calculus, and Markov.
Church’s thesis: The class of functions that can be calculated in the intuitive sense is precisely
the class of functions that can be calculated by Turing-computable (and thus while-, goto,
Queue-, 2PDA-computable, …) functions.
Homework:
1, 𝑥≥1
𝑠𝑖𝑔𝑛: 𝑁0 → 𝑁0 𝑠𝑖𝑔𝑛(𝑥) = {
0 𝑥=0
1, 𝑥 = 𝑦
𝑒𝑞𝑢𝑎𝑙: 𝑁0 𝑥 𝑁0 → 𝑁0 𝑒𝑞𝑢𝑎𝑙(𝑥, 𝑦) = {
0 𝑥 ≠𝑦
fac(𝑛) = 𝑛!
3) Calculate 𝑎𝑐𝑘(2,3) = ?