<<

1 Primitive Recursive Functions CDM Primitive 2 Properties of PR

3 Coding

Klaus Sutner 4 *Digression: G¨odel’s β Carnegie Mellon University Spring 2021

Computability, Definition 2 Knee-Jerk Reaction 3

Computable means: can be done, in principle, by a standard We need a formal definition of that digital .

is easy to understand and apply, and This sounds good to anyone who has ever written and executed a matches our intuitive notion of computability. program; sadly, there are lots of problems with this approach.

First, the hedge “in principle” means you really have to abstract away There are many plausible approaches, we’ll start with a model for from a concrete physical device (time, space, mass, energy, . . . ). functions that dates back to the 19th century and is quite straightforward. Additionally, it is more palatable that machine models to Then there is the question which operating system, which programming those used to program. language, which compiler? These typically have no clear semantics, so what exactly are we defining?

But Why? 4 Math versus CS 5

Aka computability versus .

Can’t we get away with an informal, wishy-washy definition? Implementation details are usually of little interest in , it only matters whether a function is computable or not. Computability is a Yes and No, but mostly: No. central foundational issue, but does not require detailed analysis.

Informal is typically good enough for positive results: such-and-such CS is a bit different here, computability alone is typically of interest only thing is computable. as a very first step (e.g., when one establishes the of some problem), to be followed by a careful effort to streamline the But for negative results we need real foundations: such-and-such problem computations (so as to keep resource bounds low). fails to be computable, or fails to be computable given particular resources (complexity theory). As we now understand, the latter type of question can be breathtakingly difficult. This second step leads into the realm of algorithms, which should not be confused with computable functions. Arguably, the concept of is much more complicated and currently only ill-defined notion. We’ll focus on computability. Computable Arithmetic Functions 6 Primitive Recursion 7

The main idea behind our first model is quite straightforward for anyone who has every used a modern programming language: we will define a For the time being we consider only one data type: the natural numbers function f : N Nn N by N. The corresponding functions are called arithmetic functions or number × → theoretic functions: Some examples are familiar to any kindergartener: , , squaring, roots, and so on. defining f(0, y) explicitly, and n f : N N → defining f(x + 1, y) in terms of f(x, y). We introduce a that is designed to work particularly well with these, no input/output coding is required. This should produce computable functions: we can either compute f(n, y), f(n 1, y), f(n 2, y) . . . top-down (slightly complicated, − − requires a recursion stack), or we can compute bottom- f(0, y), For the time being, our functions will be total. f(1, y), f(2, y) . . . This requires no more than a loop.

Later we will see more complicated forms of recursion.

Details: Primitive Recursive Functions 8 Composition 9

m n Given functions gi : N N for i = 1, . . . , n , h : N N , we define a → → new function f : Nm N by composition as follows: → Interestingly, G¨odel encountered the problem of defining computable f(x) = h(g1(x), . . . , gn(x)) functions working on his seminal incompleteness . He introduced a of “very simple,” easily describable functions, that are now called primitive recursive functions. Notation: we write Comp[h, g , . . . , g ] or simply h (g , . . . , g ) inspired 1 n ◦ 1 n by the the well-known special case m = 1: Recursion is the key idea, but we need a few more ingredients such as composition and projections. (h g)(x) = h(g(x)). ◦ It will always be crystal clear that our functions are intuitively computable.

It is clear that computability is closed wrto composition: output can be -used as input.

Digression: 10 Bureaucracy: Projections 11

Unfortunately, composition by itself is not quite enough. When dealing with functions or relations, it is sometimes convenient to be able to express the arity as part of the notation used. Suppose we have a binary version add of addition, and want to define a ternary version. No problem:

n (3) (2) (2) We will use a superscript ( ) for this purpose: add (x, y, z) = add (x, add (y, z)) f (n) n a function of arity But, this is not allowed according to our definition of composition; just try.

We need a simple auxiliary tool, so-called projections: (n) In particular write ca for the n-ary constant x a. 7→ n n n P : P (x , . . . , xn) = xi (0) i N N i 1 We will call ca a hard constant. → where 1 i n for the projections. ≤ ≤ Describing Functions 12 Clones 13

A clone or function algebra is a collection of arithmetic functions that contains all projections and is closed under composition. Now we can write

(3) (2) 3 (2) 3 3 More generally, for any A, define the collection of all finitary add = add (P1 , add (P2 ,P3 )) ◦ ◦ functions over A as F = (An A) A → n 0 [≥

Note that no variables are needed in this notation system. Definition

In general, we will prefer the informal notation, but you should know how A clone (over A) is a FA that contains all projections and is C ⊆ to use projections to write formally correct terms. closed under composition.

For example, all projections form a clone, as do all arithmetic functions.

Nullary Functions 14 Nullary Composition 15

Note that we allow hard constants, null-ary functions in A0 A. → m m Recall composition: f (n), g( ), i [n], produces f (g , . . . , g ) F( ) i ∈ ◦ 1 n ∈ A We will write f() or f( ) when we evaluate such functions. where n, m 0. ∗ ≥

In the literature, you will also find clones without null-ary functions It is worthwhile to consider the special case where f or the gi are nullary.

F(+) = (An A) Case: n = 0 C ⊆ A → n>0 (m) [ Then for m 1 we have cf( ) . ≥ ∗ ∈ C Case: m = 0 This is mostly a technical detail, but one should be aware of the issue. (0) Then for n 1 we have ca where a = f(g ( ), . . . , g ( )). ≥ ∈ C 1 ∗ n ∗ Actually, this is exactly the kind of pesky detail that makes programming quite so difficult.

Generating Clones 16 Rectypes 17

To get something more interesting, we need to consider clones that are generated by This is a perfect example of a recursive data type (rectype), one of the fundamental concepts in TCS. We have certain basic functions , and/or F closed under additional operations Op. a collection of atoms (indecomposable items), and

a collection of constructors that can be applied to build more We write complicated, decomposable objects. clone( ; Op) F for the least clone containing and closed under Op. Because of this inductive structure we can perform inductive , F both to establish properties and to define operations. For example, clone(; ) consists just of all projections. Basic Arithmetic Functions 18 Operations: Primitive Recursion 19

When dealing with natural numbers, it is natural (duh) to have Given h : Nn+2 N and g : Nn N we define a new function → → f : Nn+1 N by → Constant zero 0 f(0, y) = g(y) S : N N , S(x) = x + 1 → f(x + 1, y) = h(x, f(x, y), y)

(0) Here constant 0 is meant to be the hard constant c0 (but recall the Write Prec[h, g] for this function. comment on nullary composition from above).

This is a rather spartan set of built-in functions, but as we will see it’s all we need. Needless to say, these functions are trivially computable. Definition A function is primitive recursive (p..) if it lies in the clone generated by In fact, it is hard to give a reasonable description of the natural numbers zero, successor; and closed under primitive recursion: clone(0,S; Prec). without them (unless you are a set theorist).

Two Views 20 Example: Factorials 21

The standard definition of the factorial function uses recursion like so:

f(0) = 1 f(x + 1) = (x + 1) f(x) bureaucracy basic operator · To write the factorial function in the form f = Prec[h, g] we need atom projections zero, successor - g : N0 N, g() = 1 → constructors composition - prim. rec. 2 h : N N, h(u, v) = (u + 1) v → · g is none other than S 0 and h is multiplication combined with the ◦ successor function:

f = Prec[mult (S P 2,P 2),S 0] ◦ ◦ 1 2 ◦

Digging Down 22 And Addition? 23

We have used addition, which can in turn be defined by yet another To get multiplication we use another recursion: recursion.

mult(0, y) = 0 add(0, y) = y mult(x + 1, y) = add(mult(x, y), y) add(x + 1, y) = S(add(x, y))

(1) Note that the 0 here technically stands for the unary version c0 to We write x and y for simplicity, we really should use projections. conform with the specs for primitive recursion. Since S is a basic function, we now have a complete, inductive proof that (n) This is not an issue, since we know that our clone contains all ca , n 0. factorial is primitive recursive. And a way to compute factorials (at least ≥ in principle). R. Dedekind 24 Arithmetic 25

It is a good idea to go through the definitions of all the standard basic arithmetic functions from the p.r. point of view.

add = Prec[S P3, P1] ◦ 2 1 mult = Prec[add (P3, P3), 0] ◦ 2 3 2 pred = Prec[P1, 0] 3 1 sub0 = Prec[pred P , P ] ◦ 2 1 2 2 sub = sub0 (P , P ) ◦ 2 1 Since we are dealing with N rather than Z, sub here is proper : x • y = x y whenever x y, and 0 otherwise. − − ≥

Exercise These equational, inductive definitions of basic arithmetic functions date back to Dedekind’s 1888 paper “Was sind und was sollen die Zahlen?” Show that all these functions behave as expected.

Informal versus Formal 26 A Programming Language 27

Strictly speaking, in order to exhibit a p.r. function, we should write down a term in the corresponding programming language. For example, the following expression shows that the factorial function is p.r. We could think of our system as a simple programming language PR for arithmetic functions. The “programs” are just well-formed terms using Prec[Prec[Prec[S P3, P1] (P3, P3), 0] (S P2, P2), 1] the basic ingredients: projections, zero, successor, composition and ◦ 2 1 ◦ 2 3 ◦ ◦ 1 2 primitive recursion.

where we have written 1 for S 0 for legibility. The innermost Prec yields ◦ addition, the next multiplication and the last factorial. Any such term τ describes an arithmetic function τ ? : Nk N . → Of course, RP programs also form a rectype: atoms are projections, zero This is an instance of the old battle between formal and informal proofs. and successor; constructors are composition and primitive recursion. If you are a theorem prover, the formal version is far better. But it is very hard on the human eye: we will usually prefer the informal descriptions from above.

Evaluation 28

Based on RP being a rectype, it would be quite straightforward to program out an evaluation operator eval that takes as input any 1 Primitive Recursive Functions well-formed term τ of arity n and input x = x1, . . . , xn N: ∈ eval(τ, x) = value of τ ? on arguments x 2 Properties of PR

3 Coding Exercise Write a compiler that given any string τ checks whether it is a well-formed expression denoting a primitive recursive function. 4 *Digression: G¨odel’s β Function

Exercise Write an interpreter for primitive recursive functions (i.e., implement eval) in your favorite programming language. A Primitive Recursive Zoo 30 A Slog 31

We have seen that basic arithmetic functions such as addition, multiplication and proper subtraction are all primitive recursive. The results in this section are very technical, we have to formally verify In fact, it is quite difficult to come up with an arithmetic function that that certain operations on functions do not affect primitive recursiveness. fails to be primitive recursive, yet is somehow intuitively computable. Go through any basic book on number theory, everything will be p.r. Once you have gone through the technical details once, try to ignore them and focus on developing intuition that explains why a function is To show that lots of functions are primitive recursive we need two tools: primitive recursive (rather than just proving it by writing down some incomprehensible formal expression). A pool of known p.r. functions, and

strong closure properties.

Admissibility 32 Definition by Cases 33

Here is an example of a closure property that is not obvious from the definitions. Apparently, we lack a mechanism for definition-by-cases: Definition Let g, h : Nn N and R Nn. → ⊆ 3 if x < 5, Define f = DC[g, h, R] by f(x) = 2 (x otherwise. g(x) if x R, f(x) = ∈ (h(x) otherwise. We know that x 3 and x x2 are p.r., but is f also p.r.? 7→ 7→ We want to show that definition by cases is admissible in the sense that when applied to primitive recursive functions/relations we obtain another We need to explain what it means for the relation R to be primitive primitive recursive function. Having lots of admissible operations around recursive, we’ll do that in a minute. makes it easier to show that some functions are primitive recursive.

Sign and Inverted Sign 34 Relations 35

As usual, define the characteristic function of a relation R The first step towards implementing definition-by-cases is a bit strange, but we will see that the next function is actually quite useful. 1 x R charR(x) = ∈ 0 otherwise. The sign function is defined by 

sign(x) = min(1, x) to translate relations into functions.

so that sign(0) = 0 and sign(x) = 1 for all x 1. Sign is primitive ≥ Definition recursive: Prec[S 0, 0] in sloppy notation. ◦ A relation is primitive recursive if its characteristic function is primitive recursive. Similarly the inverted sign function is primitive recursive:

sign(x) = 1 • sign(x) We will use analogous definitions later for all kinds of other types of − computable functions: Turing, polynomial time, polynomial space, whatever. Equality and Order 36 Closure Properties 37

Proposition Define E : N2 N by → The primitive recursive relations are closed under intersection, union and E = sign add (sub (P2, P2), sub (P2, P2)) complement. ◦ ◦ ◦ 1 2 ◦ 2 1 Or, less formally, but more intelligible: Proof.

E(x, y) = sign((x • y) + (y • x)) − − charR S = mult (charR, charS) ∩ ◦ charR S = sign add (charR, charS) Then E(x, y) = 1 iff x = y, and 0 otherwise. Hence equality is primitive ∪ ◦ ◦ char R = sub (S 0, charR) recursive. Even better, all standard order relations such as N− ◦ ◦ =, , <, ,... 2 6 ≤ ≥ are primitive recursive (so we can use them e.g. in definitions by cases). In other words, primitive recursive relations form a , and even an effective one: we can compute the Boolean operations.

Arithmetic and Logic 38 DC is Admissible 39

Note what is really going on here: we are using arithmetic to express logical concepts such as disjunction. If g, h, R are primitive recursive, then f = DC[g, h, R] is also primitive The fact that this translation is possible, and requires very little on the recursive. side of arithmetic, is a central reason for the algorithmic difficulty of many arithmetic problems: logic is hard, by implication arithmetic is also Proof. difficult. f = add (mult (char , g), mult (char , h)) ◦ ◦ R ◦ R For example, finding solutions of Diophantine equations is hard. Less cryptically

f(x) = char (x) g(x) + char (x) h(x) R · R · Exercise Since either charR(x) = 0 and charR(x) = 1, or the other way around, Show that every finite set is primitive recursive. Show that the even we get the desired behavior. 2 numbers are primitive recursive.

Bounded Sum 40 Exercises 41

Proposition Let g : Nn+1 N be primitive recursive, and define Exercise → Repeat the proof for products. f(x, y) = Σz

Then f : Nn+1 N is again primitive recursive. The same holds for → products. Exercise Show that f(x, y) = g(z, y) z < x R(z) is primitive recursive | ∧ Proof. when g and R are primitive recursive. P  f = Prec[add (g (P n+2,P n+2,...,P n+2),P n+2), 0n] ◦ ◦ 1 3 n+2 2 Less formally, Exercise

Show that f(x, y) = z

Here we have written x+ instead of x + 1. Yes, that helps. Bounded Search 42 Keeping Things Simple 43

A particularly important algorithmic technique is search over some finite domain. For example, in brute-force factoring n we are searching over an interval [2, n 1] for a number that divides n. Or in a chess program we search Note that f(x, y) = x simply means that the search failed. In a more − for the optimal next move over a space of possible next moves. luxurious environment we might set a flag, throw an exception or some We can model search in the realm of p.r. functions as follows. such.

Here we want everything to be a simple as possible, and in particular Definition (Bounded Search) constrained to pure arithmetic. So we code failure as a numerical value, Let g : Nn+1 N . Then f = BS[g]: Nn+1 N is the function defined basta. → → by min z < x g(z, y) = 0 if z exists, f(x, y) = | (x  otherwise.

BS is Admissible 44 Bounded Search II 45

This can be pushed a little further: the search does not have to end at x. One can show that bounded search is also admissible, it adds nothing to Instead, we can search up to a primitive recursive function of x and y. the class of p.r. functions.

min z < h(x, y) g(z, y) = 0 if z exists, Proposition f(x, y) = | (h(x,y)  otherwise. If g is primitive recursive, then so is BS[g].

Dire Warning: But we have to have a p.r. bound, unbounded search as in Exercise f(y) := min z g(z, y) = 0 Show that bounded search is indeed admissible (“primitive recursive | functions are closed under bounded search”). is not an admissible operation; not even when there is a suitable witness z for each y. See the discussion of the µ-operator later.

Example: Primality 46 Primality 47

Claim (2) Claim (1) The primality relation is primitive recursive. The divisibility relation div(x, y) is primitive recursive.

To see why, note that x is prime iff Note that div(x, y) z y (x z = y) 1 < x z < x (div(z, x) z = 1). ⇐⇒ ∃ ≤ ∗ ∧ ∀ ⇒ so that bounded search intuitively should suffice to obtain divisibility. Formally, we have already seen that the characteristic function M(z, x, y) of x z = y is p.r. But then ∗ The building blocks 1 < x, div and z = 1 are all p.r., and we can sign M(z, x, y) combine things by and . The only potential problem is the z y ∧ ⇒ ≤ (bounded) universal quantifier. X  is the p.r. characteristic function of div. But this is quite similar to the situation with div from the last slide. Time for a general solution. Yet More Logic 48 Bounded Quantification 49

Bounded quantification is really just a special case of bounded search: for Arguments like the ones for basic number theory suggest another type of P (x, y) we search for a witness z < x such that P (z, x, y) holds. closure properties, with a more logical flavor. ∃ Generalizes to z < h(x, y) P (z, x, y) and z < h(x, y) P (z, x, y). ∃ ∀ Definition (Bounded Quantifiers) Proposition P (x, y) z < x P (z, x, y) and P (x, y) z < x P (z, x, y). Primitive recursive relations are closed under bounded quantification. ∀ ⇔ ∀ ∃ ⇔ ∃ Proof. Note that P (0, y) = true and P (0, y) = false. ∀ ∃ charP (x, y) = charP (z, x, y) ∀ Informally, and using the dreaded ellipsis, z

charP (x, y) = sign charP (z, x, y) P (x, y) P (0, x, y) P (1, x, y) ... P (x 1, x, y) ∃ ∀ ⇐⇒ ∧ ∧ ∧ − z

Next Prime 50 Enumerating Primes 51

Claim (3) The next prime function f(x) = min z > x z prime is p.r. Claim (4) |  The function n pn, where pn is the nth prime, is primitive recursive. 7→

This follows from the fact that we can bound the search for the next To see this we can iterate the “next prime” function from the last claim: prime by a p.r. function: p(0) = 2 f(x) 2x for x 1. ≤ ≥ p(n + 1) = f(p(n)) This bounding requires a little number theory. In general, the theory of gaps between consecutive primes is quite difficult (consider prime twins), but this result is not too bad.

Exercises 52

1 Primitive Recursive Functions

Exercise 2 Properties of PR Show in detail that the function n p where p is the nth prime is 7→ n n primitive recursive. How large is the p.r. expression defining the function?

3 Coding Exercise Find some other closure properties of primitive recursive functions. 4 *Digression: G¨odel’s β Function Faking Data structures 54 Leopold Kronecker, Semi-Constructivist 55

Our primitive recursive programming language has one glaring defect: it only supports one data type, N. There are no lists, trees, graphs, hash tables and so on, only natural numbers.

As it turns out, all these discrete structures can be obtained from just Die ganzen Zahlen hat der liebe Gott integers if we are able to express sequences a0, a1, . . . , an 1 of numbers gemacht, alles andere ist Menschenwerk. − as a single number a0, a1, a2, . . . , an 1 . h − i “Dear god” made the integers, This is obviously not meant as a practical programming idea, it is purely everything else is the work of men. conceptual: natural numbers already suffice in principle, and the ability to compute with them means that other computation involving, say, list, are also possible.

Coding 56 Decoding 57

Suppose Write N? for the set of all finite sequences of natural numbers and nil for b = a0, a1, a2, . . . , an 1 the empty sequence. h − i

? is some code number. Note that we have used 0-indexing to simplify We would like to express a sequence a0, a1, . . . , an 1 N as a single − ∈ notation below. number a0, a1, . . . , an 1 . So we need a coding function, a polyadic h − i map of the form We want a unary length function len : N N that determines the length → . : N∗ N of the coded sequence h i → len(b) = n that allows us to decode: from b = a0, a1, . . . , an 1 we can recover n h − i as well as all the ai. and a binary decoding function dec : N N N that extracts the × → components: Note that the coding function must necessarily be injective. Moreover, both the coding and decoding operations should be computationally dec(b, i) = ai cheap, at least primitive recursive. for all i = 0, . . . , n 1 . Traditionally, dec(b, i) is written (b) . − i

Computable Coding 58 Sequence Numbers 59

Again, we need three functions:

The numbers of the form a0, a1, a2, . . . , an 1 that appear as codes of h − i sequences are called sequence numbers. ? . : N N h i → Note that a priori len(x) need not be defined when x is not a sequence dec : N N N × → number. The same is true for dec(x, i), plus i may be too large to make len : N N sense. Still, one usually insists that both decoding functions are total and → return some default value like 0 for meaningless arguments.

In the set-theoretic universe, the existence of these functions is entirely Exercise ? trivial: N is countable. Show how to check if a number is a sequence number given dec and len. But we live in the computational universe: we need these functions to be easily computable, and in particular primitive recursive. Pairs 60 In Binary 61

The first step is to select a pairing function, an injective map π : N N N . × → Note that the binary expansion of π(x, y) looks like so: Equivalently, we are looking for 3 functions π : N N N , πi : N N , × → → i = 1, 2, such that ykyk 1 . . . y0 1 00 ... 0 πi(π(x1, x2)) = xi − x

where ykyk 1 . . . y0 is the standard binary| expansion{z } of y (yk is the most There are many possibilities, the following choice arguably yields one of − significant digit). Hence the range of π is N+ (but not N). the most intuitive coding functions.

x π(x, y) = 2 (2y + 1) This makes it easy to find the corresponding unpairing functions:

x = π1(π(x, y)) y = π2(π(x, y)). For example

π(5, 27) = 32 55 = 1760 = 110111 00000 · 2

Aside: Fueter-P´olya 62 Extending to Sequences 63

Another popular pairing function is the quadratic polynomial due to Cantor: p(x, y) = ((x + y)2 + 3x + y)/2 nil := 0 h i Note that this function is a bijection (unlike our exponential pairing a0, . . . , an 1 := π(a0, a1, . . . , an 1 ) h − i h − i function which misses 0).

Here are some sequence numbers for this particular coding function: A surprising theorem by Fueter and P´olya from 1923 states that, up to a swap of variables, this is the only quadratic polynomial that defines a 10 = 1024 bijection N2 N. h i ↔ 0, 0, 0 = 7 h i The proof is rather difficult and uses the fact that ea is transcendental 1, 2, 3, 4, 5 = 532754 for algebraic a = 0. h i 6 It is an open problem whether there are other bijections for higher degree polynomials. Extra Credit.

It’s a Bijection 64 Less formally . . . 65

Lemma . : N∗ N is a bijection. h i → Here is a sequence number and its binary expansion:

Proof. Suppose 2, 3, 5, 1 = 20548 h i a0, . . . , an 1 = b0, . . . , bm 1 h − i h − i = 1 0 1 00000 1 000 1 00 We may safely assume 0 < n m (why?). 1 5 3 2 ≤ Since π is a pairing function, we get a0 = b0 and |{z} | {z } |{z} |{z} a1, . . . , an 1 = b1, . . . , bm 1 . h − i h − i So the number of 1’s (the digitsum) is just the length of the sequence, By induction, a = b for all i = 1, . . . , n 1 and and the spacing between the 1’s indicates the actual numerical values. i i − 0 = nil = bn, . . . , bm 1 . Hence n = m and our map is injective. h i h − i It follows that the coding function is injective and surjective, right? Exercise Prove that the function is surjective. That’s It! 66 Everything is PR 67

We can now code any discrete structure as an integer by expressing it as a nested list of natural numbers, and then applying the coding function. Exercise Show that the pairing function π and both unpairing functions For example, the so-called Petersen graph on the left is given by the x = π (π(x, y)) and y = π (π(x, y)) are primitive recursive. nested list on the right. 1 2

Exercise Show that the length and decoding functions len and dec are primitive recursive. ((1, 3), (1, 4), (2, 4), (2, 5), (3, 5), (6, 7), (7, 8), (8, 9), (9, 10), (6, 10), Exercise Show that the coding function . is primitive recursive when restricted to (1, 6), (2, 7), (3, 8), (4, 9), (5, 10)) h i inputs of fixed length.

Recording History 68 Course of Value Recursion 69

One neat application of sequence numbers is course-of-value recursion. First note that ordinary primitive recursion can be expressed in terms of Thus, it is natural to generalize the primitive recursion scheme slightly by sequence numbers like so: defining functions so that the value at x depends directly on all the previous values. + f(x, y) = z s Seq len(s) = x (s)0 = g(y) ⇐⇒ ∃ ∈ ∧ ∧ f(0, y) = g(y) 0 i < x ((s)i+ = h(i, (s)i, y)) (s)x = z ∀ ≤ ∧ f(x+, y) = H(x, f(x, y), y)  Here x+ is shorthand for x + 1. The sequence number s records all b previous values of f. Now consider the following function associated with Lemma f: If g and H are primitive recursive then f is also primitive recursive.

f(x, y) := f(0, y), f(1, y), . . . , f(x, y) h i Exercise b Prove the last two lemmata. You may safely assume that standard Lemma sequence operations such as append are primitive recursive. f is primitive recursive iff f is primitive recursive.

b

Observation 70

1 Primitive Recursive Functions Claim In the RealWorldTM, every is already primitive recursive. 2 Properties of PR

3 Coding

This is, of course, nothing like a theorem, just a practical observation: natural computable functions have a very strong tendency to be primitive recursive. 4 *Digression: G¨odel’s β Function

All counterexamples to this rule are somehow “artificial.” G¨odel’sApproach 72 G¨odel’sTrick 73

There is more elegant and slightly more way to code sequence To deal with sequences of arbitrary length one can use a clever divisibility numbers due to G¨odel that he used in his famous incompleteness argument. theorem. Lemma (Godel)¨ There exists a primitive recursive function dec : N2 N such that →

a0, . . . , an 1 a i < n (ai = dec(a, i)). ∀ − ∃ ∀

So a is a potential code number for a0, . . . , an 1 −

Proof. Set

dec(a, i) = min x < a (π(x, i) + 1)π (a) + 1 divides π (a) | 2 1   The idea is that the factors of π1(a) contain information about the ai. For the sake of completeness, here is a brief description of G¨odel’s We need to establish the existence of the witness a. method.

Proof, contd. 74 Sequence Numbers 75

Definition Let a0, . . . , an 1 arbitrary and set Define a coding function . by − h i c = max π(ai, i) i < n x = min a dec(a, 0) = n i [n](dec(a, i) = a ) | h i | ∧ ∀ ∈ i C = (c 1)!   − Also set lh(a) = dec(a, 0) and (a)i := dec(a, i). p = ((π(ai, i) + 1)C + 1) i

TM Sequence Operations 76 Algorithms in the RealWorld 77

As always, having a data structure by itself is not particularly interesting, We claim that any algorithm you will ever see, outside of a class dealing we need to be able to implement operations. In our case, one can show directly with logic and computability, is always primitive recursive. And, that the following operations on sequences are primitive recursive. in fact, trivially so.

head, tail There are two parts to this claim: concatenate reverse All these algorithms operate on finitary data structures that can be coded naturally as sequence numbers, and sort given this natural coding, for input as well as output, the map corresponding functions are always primitive recursive. sum, product

In fact, it would be quite difficult to come up with any example of an Of course, there is no actual theorem here, just an observation. I’d be operation used in a real program that fails to be primitive recursive. most curious to hear about anything that might contradict this claim. PRec Bijections 78 Contd. 79

Theorem (Kuznecov 1950) Let R be the range of B : N N , so R is infinite, co-infinite and → The collection of bijective primitive recursive functions is not closed primitive recursive. Note that R is very sparse. under inverse.

Let πX be the Hauptfunktion of X N and define f : N N ⊆ → Proof. Define the Ackermann-like function 1 2πR− (x) if x R, f(x) = ∈ B0(x) = 2x 1 2π− (x) + 1 otherwise. x R Bn+ (x) = Bn(1) 

B(x) = Bx(x)  1 Then f is an primitive recursive bijection, but f − fails to be primitive recursive. It follows from monotonicity that the predicate “Bn(x) = y” is primitive recursive, uniformly in n, x, y.

Exercises 80

Exercise Prove that all these functions are indeed primitive recursive.

Exercise Explain how to implement search in binary search trees as a primitive recursive operation.

Exercise Come up with yet another coding function based on repeated application of a pairing function (make sure your method really works).