6. Recursively defined sets and structural induction Now we`re going

6. Recursively defined sets and structural induction
Now we’re going to switch to a different topic. This will reconnect to Turing machines in a bit.
A recursive definition has two components:
• a collection of base elements; and
• a collection of rules for obtaining new elements from old elements.
For example, consider the set B of numbers given by the following:
• 6 ∈ B;
• 8 ∈ B;
• If a, b ∈ B, then a + b ∈ B.
Then B contains 6, 8, 12, 14, 16, 18, . . . .
Instead of numbers, we might make a set of strings A:
• 00 ∈ A;
• If σ ∈ A, then 0σ0 ∈ A;
• If σ ∈ A, then 11σ11 ∈ A.
Then A contains 00, 0000, 110011, 11000011, 01100110, . . . .
The natural numbers can also be given a recursive definition:
• 0 ∈ N;
• If n ∈ N, then n + 1 ∈ N.
Returning to the first example, note that N obeys that recursive definition: 6, 8 ∈ N and for any a, b ∈ N,
a + b ∈ N. However, we don’t intend that recursive definition to refer to N; we only want those elements
which are generated from the base elements by applying the rules. Formally, the recursively defined set is
the smallest set which contains the base elements and is closed under the rules. This can be defined in two
ways, either as an intersection from above or a union from below. Suppose we have a recursive definition as
follows:
• a0 , a1 , . . . , ak ∈ C;
• If x0 , . . . , xn ∈ C, then Φ0 (x0 , . . . , xn ), . . . , Φm (x0 , . . . , xn ) ∈ C.
Then we can define
\
C=
D Ja0 , . . . , an ∈ D & (∀x0 , . . . , xn ∈ D) Φ0 (x0 , . . . , xn ), . . . , Φm (x0 , . . . , xn ) ∈ DK.
Alternatively, we can define
C0
=
{a0 , a1 , . . . , ak }
Ci+1
=
C
=
{Φj (x0 , . . . , xn ) : x0 , . . . , xn ∈ Ci & j ≤ m} ∪ Ci
[
Ci
i∈N
You should convince yourself that these two definitions give the same set C, and that C obeys the recursive
definition.
One nice thing about recursively defined sets is that you can do structural induction on them. Suppose
you have a property P and a recursively defined set A, and you want to show that all elements of A have
property P . You can show this by showing that all the base elements have property P (the base cases), and
that if an element is made using one of the rules from elements which all have property P , then the new
element also has property P (the inductive steps). This type of proof is called structural induction.
Example 6.1. All elements of B are even.
Here the property is “being even”. First we need to check the base cases, which means showing that the
base elements are even. But the base elements are just 6 and 8, which we know are even.
Next we need to do the inductive step. Since our rule is “new elements are made by adding two old ones
together”, we need to look at elements which are made in this way. Suppose c = a + b, and a and b are both
even. We must show that c is even. But we know that the sum of two even numbers is even, so we’re done.
Example 6.2. All elements of A are palindromes.
Here the property is “being a palindrome”. Before we start, let’s introduce some useful notation: if σ is
a string, then σ ∗ is σ reversed. So (010011)∗ = 110010. Notice that saying σ is a palindrome is the same as
saying σ = σ ∗ . Also, notice that (στ )∗ = τ ∗ σ ∗ .
Base case: 00 is a palindrome.
Inductive step: If σ is a palindrome, then consider 0σ0. (0σ0)∗ = 0∗ σ ∗ 0∗ = 0σ ∗ 0 = 0σ0. This last step is
because σ is a palindrome. So then 0σ0 is a palindrome.
Inductive step: If σ is a palindrome, then consider 11σ11. (11σ11)∗ = (11)∗ σ ∗ (11)∗ = 11σ ∗ 11 = 11σ11.
So 11σ11 is a palindrome.
Example 6.3. Structural induction on N (with the recursive definition given above) is the same as normal
induction.
7. Partial recursive functions
Now we introduce a completely different model of computation. Instead of looking at constructing some
sort of device and looking at the functions that this device can compute, we build up a class of computable
functions from simple parts. This model might seem very strange, but it is actually the oldest model of
computable functions. It is one of the ancestors of all functional programming languages, such as Scheme.
8. Primitive recursive functions
First we start with a smaller class. Later we will see that it is missing one key ingredient, which we will
add.
Definition 8.1. The primitive recursive functions is the set of functions P given by the following recursive
definition:
•
•
•
•
•
The zero function Z(x) = 0 is in P.
The successor function S(x) = x + 1 is in P.
The predecessor function P (x) = x−̇1 is in P.
Every projection function Pj (x0 , . . . , xm−1 ) = xj is in P.
Substitution. If g(y0 , . . . , ym−1 ) ∈ P, and fi (x0 , . . . xk−1 ) ∈ P for every i < m, then
g(f0 (x0 , . . . , xk−1 ), . . . , fm−1 (x0 , . . . , xk−1 )) ∈ P.
• Primitive Recursion. If g(x0 , . . . , xm−1 ) ∈ P and h(x0 , . . . , xm+1 ) ∈ P , then the function f defined
below is also in P:
– f (x0 , . . . , xm−1 , 0) = g(x0 , . . . , xm−1 );
– f (x0 , . . . , xm−1 , n + 1) = h(x0 , . . . , xm−1 , n, f (x0 , . . . , xm−1 , n)).
−̇ is the monus operation. Monus is the same as minus, except if the answer would be negative, it instead
gives 0.
Notice that we have the identity function f (x) = x. It’s a projection function: P0 (x).
Substitution looks messy, but it’s just composition of functions.
Example 8.2. f (x) = 1 is primitive recursive.
By substitution, S(Z(x)) is primitive recursive, and S(Z(x)) = Z(x) + 1 = 0 + 1 = 1.
Example 8.3. f (x) = x + 2 is primitive recursive.
2
By substitution, S(S(x)) is primitive recursive, and S(S(x)) = S(x) + 1 = x + 1 + 1 = x + 2.
Primitive recursion is trickier, but it lets us build up functions one step at a time.
Example 8.4. f (x, y) = x + y is primitive recursive.
Let g(x0 ) = P0 (x0 ) = x0 . Let h(x0 , x1 , x2 ) = S(P2 (x0 , x1 , x2 )) = x2 + 1. Let f be the function given by
• f (x0 , 0) = g(x0 );
• f (x0 , n + 1) = h(x0 , n, f (x0 , n)).
By primitive recursion, f is primitive recursive. Next we should verify that f really does addition. We
do this by induction on n.
f (x, 0)
=
g(x)
= P0 (x)
= x
= x + 0.
f (x, n + 1)
=
h(x, n, f (x, n))
= S(P2 (x, n, f (x, n)))
= P2 (x, n, f (x, n)) + 1
= f (x, n) + 1
=
(x + n) + 1
= x + (n + 1).
The important point here is that addition is just repeatedly adding 1; a + b can be calculated by starting
with a, then applying S b-many times. That is basically what this construction by recursion is doing. We
start with f (x, 0) = x, then we increase the second input from 0 to y one step at a time, adding 1 to the
output with each step.
Example 8.5. f (x, y) = xy is primitive recursive.
Let g(x0 ) = Z(x0 ) = 0. Let h1 (x0 , x1 ) = x0 + x1 , which we know is primitive recursive by the previous
example. Let h(x0 , x1 , x2 ) = h1 (P0 (x0 , x1 , x2 ), P2 (x0 , x1 , x2 )) = x0 +x2 , which we know is primitive recursive
by substitution. Let f be the function given by
• f (x0 , 0) = g(x1 );
• f (x0 , n + 1) = h(x0 , n, f (x0 , n)).
By primitive recursion, f is primitive recursive. Next, we should verify that f really does multiplication.
We do this by induction on n.
f (x, 0)
f (x, n + 1)
=
=
g(x)
=
Z(x)
=
0
=
x · 0.
h(x, n, f (x, n))
= h1 (P0 (x, n, f (x, n)), P2 (x, n, f (x, n)))
= P0 (x, n, f (x, n)) + P2 (x, n, f (x, n))
= x + f (x, n)
= x+x·n
= x(n + 1).
3
Just like addition is repeatedly adding 1, multiplication is repeated addition; ab can be calculated by
starting at 0, then adding a b-many times. Again, this is what the recursive construction is doing.
Example 8.6. f (x) =
x(x+1)
2
is primitive recursive.
Let g(x1 ) = Z(x1 ) = 0. Let h1 (x0 , x1 ) = x0 +x1 . Let h(x0 , x1 , x2 ) = S(h1 (P1 (x0 , x1 , x2 ), P2 (x0 , x1 , x2 ))) =
x1 + x2 + 1, which we know is primitive recursive by substitution. Let f1 be the function given by
• f1 (x0 , 0) = g(x0 );
• f1 (x0 , n + 1) = h(x0 , n, f1 (x0 , n)).
By primitive recursion, f1 is primitive recursive. Let f (x) = f1 (Z(x), P0 (x)) = f1 (0, x), which we know
is primitive recursive by substitution. Now we need to verify that f is really the function we want. We do
this by induction on n.
f (0)
= f1 (Z(0), P0 (0))
=
f1 (Z(0), 0)
=
g(Z(0))
=
Z(Z(0))
=
0
0(0 + 1)
.
2
=
f (n + 1)
=
f1 (Z(n + 1), P0 (n + 1))
= f1 (0, n + 1)
= h(0, n, f (0, n))
= S(h1 (P1 (0, n, f (0, n)), P2 (0, n, f1 (0, n))))
= h1 (P1 (0, n, f (0, n)), P2 (0, n, f1 (0, n))) + 1
= P1 (0, n, f (0, n)) + P2 (0, n, f1 (0, n)) + 1
= n + f1 (0, n) + 1
= n + f1 (Z(n), n) + 1
= n + f (n) + 1
n(n + 1)
+1
= n+
2
2(n + 1) + n(n + 1)
=
2
(n + 1)(n + 2)
=
.
2
Here things were more complicated for two reasons. We started with f1 (x, 0) = 0, but then instead of
adding x each time we increased the second input, we added n, the value of the second input. (Here we’re
using the fact that n(n+1)
= 1 + 2 + 3 + · · · + n.) Also, we had to use primitive recursion to make an
2
intermediate function f1 , then use f1 to make f . This is because f is supposed to have only one input, but
primitive recursion only makes functions with at least two inputs. So we made a function f1 that ignores its
first input (you can check this yourself), then made f by just plugging zero into the first input of f1 .
Primitive recursive functions were the first attempt at classifying which functions can be done with
algorithms. As we said before, they don’t contain enough functions, but hopefully you can see why they
can all be done by algorithms. Later, we will show that every primitive recursive function is indeed Turing
computable.
4