Lec 21: Encodings & Extended Recursion Schemes - CSCI 381 - Goldberg
Administrative — Upcoming Schedule
Section titled “Administrative — Upcoming Schedule”Same as that of Lecture 20
Encodings
Section titled “Encodings”In previous classes we assumed that primitive recursion is based on induction and as such, just as simple induction has one base case and access to one previous level’s output, so too recursion. The question was asked how does one deal with a function such as Fibonacci:
Davis realized that the manner to prove that Fibonacci (and any recurrence relation) can be consistent with the formal definition of primitive recursion, is to encode all the previous levels’ outputs into a single number and then formulate a primitive recursive recursion based on that one number.
Godel (with others) suggested three genres of mathematical encodings:
a) Pairing Function based on Odd/Even and Exponentiation
Section titled “a) Pairing Function based on Odd/Even and Exponentiation”A pairing function encodes two numbers as one single number. This encoding is “lossless”, which guarantees us that we can retrieve the original data values without error. As opposed to “lossy” which is used by JPEG to encode images and compresses the data in a manner that cannot guarantee the retrieval of the original exact data. The reason that this approach is acceptable for imaging is that psychological studies have shown that the brain imaging system is not as discerning for each dot of color (unless you make a special effort to do so) but is rather interested in explaining away the image in the simplest possible manner. The theory however is concerned with lossless encoding (related to compression, but the distinction being that the theoreticians would accept an encoding that expands the data to a longer amount of data and does not demand per se the compression of the data). The theoreticians are only interested in how many numbers: 1 vs. 2.
Consider two numbers and that will be encoded by the pairing function into one number :
For example, .
The factor extracts all of the “evenness” and what necessarily remains is an odd number, hence . So, using the even/odd dichotomy the pairing function was born. Since computer theory ultimately means to encode storage of memory (cf. the definition of snapshot where indicates which line of a program is about to be executed and is an encoding of the storage of variables and their values; originally on a Turing tape, but eventually in RAM), every configuration of memory has to be explained as to its encoding and has to be lossless (i.e., encode and decode totally).
The problem is that the pairing function is always greater than zero, and hence positive, so that the encoding that results in the value zero cannot be decoded. In order to ensure that the encoding and decoding process are total functions, many of the encodings suggested in the theory literature had to subtract a constant from the encoding formula to guarantee that the value zero is covered. Here, we have:
Example: ,
- (Godel)
- (Davis, )
| Godel: | |
| Davis: |
Now, the revised pairing function allows for and as well . corresponds to the initial memory configuration where the memory has all zeros: and . That can only occur when .
b) General Godel Encoding based on Prime Number Factorization
Section titled “b) General Godel Encoding based on Prime Number Factorization”Note: This example uses four data values, but the encoding works for any number of values.
| Prime | Exponent | Contribution |
|---|---|---|
| Product |
Since computer theory wants a bijective mapping from and starts with , the above formula has to be adjusted by . Multiplying primes to nonnegative integers can only generate and above, not :
The obvious problem with this is that it is ambiguous because any sequence of zeroes will yield the same value:
So how do you know how many zeroes are encoded? They then had to LIMIT the number of ending zeroes as follows: only will be allowed to encode zeroes alone, and if the encoding has nonzero elements, the last element of the sequence cannot be zero.
Otherwise etc. But, , so zeroes before the last positive number do not cause an ambiguity; the resulting value is different each time. But zeroes after the last positive number in the encoding sequence do cause ambiguity since the value of the coding does not change: etc.
c) Godel Sequence Number - GLE (Godel Length Encoding)
Section titled “c) Godel Sequence Number - GLE (Godel Length Encoding)”This encoding inspired RLE (Run Length Encoding) on images, discussed below.
To demonstrate why an encoding does not have to be a compression: the Godel numbering sequence was a suggestion to encode any list or sequence of numbers. Suppose we have and we would like to encode them into one number. The convention was to use the number as a delimiter that indicates where the data field starts and ends, and occupies the data value itself (but here in a very specific manner). Note: the spaces are for legibility only and not part of the encoding.
| Segment | 1 00 | 1 000 | 1 00000 | 1 |
|---|---|---|---|---|
| Value | (end) |
Encoded string: 10010001000001
This works in theory because Turing machines have no limitation on storage length due to the infinite length of their tape for storage.
In the research, this encoding was generally avoided. This situation, while mathematically correct, would violate analysis of algorithm principles. GLE is essentially a unary (base-1) encoding: the value of a segment is encoded as its length in zeros, so value and length are one and the same. This is in contrast to base 2 or higher, where only bits are needed to encode a value (or equivalently, bits can represent values up to ). NOTE: That itself would not have bothered the theoreticians. They were never concerned about complexity but about solvability. They were concerned with “Can it be done?” as opposed to how do you do it (constructively) or even how well can you do it. ALSO, the decoding function from binary string to natural numbers is not total — not every binary string corresponds to a valid GLE encoding (e.g., a string starting with 0, or one with an incomplete segment).
RLE - Run Length Encoding
Section titled “RLE - Run Length Encoding”Those inspired by GLE created the RLE compression algorithm. RLE works best if you are anticipating long sequences of the same data value, in which case, the long sequence can be replaced by: [How many times did a given character appear consecutively in the sequence?][The character referred to.]
| Original | Compressed |
|---|---|
aaaaaaaaaaaaa (13 a’s) | 13a |
NOTE: The compressed version 13a is stored actually in two bytes and the character is represented by its 8-bit ASCII code. So, if the string had 13 1’s, the compressed version would be 131, BUT this would not be ambiguous since each part of this compressed/encoded segment would be stored in its own byte: Byte#1: 13 and Byte#2: ASCII code for character "1".
RLE is not the most widely used standalone compression algorithm, but it is used in practice as a second-pass compression: some lossless compression algorithms produce output data where consecutive values tend to be the same or similar, so RLE can be applied as a second round to compress the result even further.
Although the above and future suggestions can encode any number of numbers, Godel still suggested that if you need to encode only two numbers, use the Pairing function, and if you need to encode 3 or more numbers, use the “Godel (prime number) encoding”. This distinction probably emanated from Cantor who describes the theory for encoding (“counting”) the number plane (two dimensions) as single numbers.
d) Polynomial Encoding (Cantor; Chowla)
Section titled “d) Polynomial Encoding (Cantor; Chowla)”Why did Godel settle on an exponentiation formula for Pairing and Prime encodings and not come up with a polynomial or multinomial encoding?
Cantor did consider this and came up with the following polynomial encoding (1873):
where is the choose function ( choose ).
This polynomial encoding does map the Natural number plane to single natural numbers. (Note: Since , the minus constant is not necessary here.) I believe Cantor thought that there should be a whole family of polynomial encodings but he was not able to find any other polynomial encoding. Concerned with this, he avoided using this and suggested the above Pairing function, which is what theoreticians eventually all used.
Fueter and Polya (1923) proved that the above polynomial is the ONLY possible degree two polynomial that can accomplish this encoding. They conjectured that in fact the above polynomial is the only possible polynomial of any degree that can accomplish such an encoding (i.e., that every possible number(s) can be encoded and decoded).
e) Combinatoric Encoding
Section titled “e) Combinatoric Encoding”Chowla (1961) showed a generalization of the above polynomial using the equivalent combinatoric formulation that can extend to encoding any number of dimensions into a single number. Let and , and (the number of variables). Then, the above combinatorics will be rewritten as . They generalized and proved an extended combinatorics that can encode any number of numbers as follows:
Consider encoding variables by the following combinatoric polynomial:
Just as Chowla (mentioned above) was able to generalize Cantor’s Combinatoric Polynomial to encode numbers, one can also generalize the Pairing Function (which uses exponentiation) to encode numbers, but it can be quite involved. I leave this as an open challenge for intrigued students. If you do obtain a solution, please write it up and email me. I would be interested to review it and compare it to the solution I have.
f) Beta Encoding
Section titled “f) Beta Encoding”It has the advantage of being easier to describe than any efficient scheme. It requires only one arithmetic operation (mod), and it has a very short definition.
Godel’s function:
function lemma. For any sequence of natural numbers , there are natural numbers and such that for every natural number , . In summary, the beta function encodes any finite sequence of natural numbers with two natural numbers and where the th number in the sequence is generated by the beta function with the specific and computed. In practice, a third value — the length of the encoded sequence — is also needed, since can produce a value for any index . Without knowing , one cannot determine where the encoded sequence ends. So practically speaking, three values , , and are used together to fully encode and recover a sequence.
See Godel’s Beta Function Lemma proof on ProofWiki for a proof.
As an example, the sequence can be encoded by and (where , , ). Note that the example below uses the equivalent form , since :
See also Godel’s function on Wikipedia.
Godel was interested in this encoding to present his “incompleteness theorem.” Any system of logic proofs (“theorems” or truths) that includes all theorems regarding (Peano) arithmetic is either incomplete or inconsistent. As we discussed, anything that can be represented by a computer is represented by a finite string of 0/1s, which is a natural number. Thus, any listing of such theorems or truths can be encoded by the beta function. This made Godel’s counting arguments about the proposed system easier.
Formally, the “incompleteness theorem” states:
-
- No consistent system of axioms whose theorems can be listed by an effective (algorithmic) procedure is capable of proving all truths about the arithmetic of natural numbers.
- The above system cannot demonstrate its own consistency.
Finally, Ackermann’s function cannot be helped by these encodings - it simply grows faster than any primitive recursive function. As such this function is said to dominate or majorize any primitive recursive function.
Course of Values Recursion
Section titled “Course of Values Recursion”Suppose you have a primitive recursive function that has already generated the outputs . The recursive step in function computes:
for some predetermined primitive recursive function . This requires access to all previous levels’ outputs. If we incorporate Godel Prime Encoding, let:
then we can rewrite the above as follows:
where encodes the previous levels’ output values. Note: is used instead of the original since operates on the actual previous levels’ outputs but must decode first.
And then:
and:
In essence, Godel Prime Encoding will encode all previous levels’ outputs by raising the prime generated at the current level to the power of the previous level’s output and “tack that on” to the Godel Prime Encoding passed along so far by simply multiplying this powered value by the previous Godel Prime Encoding obtained.
Pairing Functions Applied to Fibonacci
Section titled “Pairing Functions Applied to Fibonacci”Now, let’s apply pairing functions to the Fibonacci sequence. The Fibonacci sequence is defined as:
The actual values for the Fibonacci sequence are
To avoid the issues raised by Fibonacci, we encode the two base cases and subsequently encode the two previous levels’ outputs by a pairing function which results in a simulated recursion on one base case and one previous level’s output. will be this auxiliary encoded function.
In computer theory, we always start with the base case. Here we want to encode the two Fibonacci base cases as a single number. In general (recursively) we want to encode the two latest Fibonacci numbers in the sequence.
Consider the first seven encodings by the pairing function. In actuality a total of eight Fibonacci sequence numbers are located in these seven encodings:
| Level | Encoding | Left | Right |
|---|---|---|---|
| 0 | 0 | 1 | |
| 1 | 1 | 1 | |
| 2 | 1 | 2 | |
| 3 | 2 | 3 | |
| 4 | 3 | 5 | |
| 5 | 5 | 8 | |
| 6 | 8 | 13 |
What emerges is that the left side consistently encodes the actual Fibonacci sequence. The last number on the Right side is actually the 8th Fibonacci number in sequence. This occurs since the Right encoded sequence does not start with the first number on the Right as 0, but instead starts with the second Fibonacci sequence number as 1. But then, the remaining numbers are the next numbers in the Fibonacci sequence (“next” as in the latest number in the sequence after the value encoded on the Left).
In general, the encoding has . Calling the encoding , we have:
This is the encoded Fibonacci which uses the pairing function encoding/decoding so that this primitive recursive function only requires one base case and one previous level’s output.
Since , the th Fibonacci number is simply and the th is . In general: for , or equivalently for all . The only thing else to specify is the one base case for this new Fibonacci function: .
Putting this all together, the new FIBONACCI function is defined as:
The pairing function both allows for encoding and decoding. The decoding processes are simply given the names “left” and “right” functions and in the literature are further simplified, written as and for left and right respectively. So, left decodes into the original and right decodes into the original :
The Postal Stamp Problem
Section titled “The Postal Stamp Problem”Ackermann’s function was not primitive recursive because it REQUIRED recursing on two variables. There exists a genre of problems in discrete mathematics that most discrete mathematics books present as if recursion/induction is required over two variables. The name of this group of problems is called the Postal Stamp problems.
Given two denominations (face values) of stamps, prove that any postage a specific fixed value can be comprised of some (independent) multiple of each denomination of stamp. In fact, there is a somewhat trivial way to rewrite the same problem such that the recursive/inductive step only requires multiples of the smaller of the two face values.
The requirement for two denominations is only to set up the multiple base cases (which will be in number equal to the face value of the smaller valued stamp denomination) and then based on modular arithmetic only multiples of this smaller value is required to obtain any other further amount of postage. And, as mentioned in previous classes, computer theory computes recursion in a bottom-up manner so that the base case actually occurs PRIOR to any recursive level is computed.
For example, any postage greater than or equal to 18 can be made by multiples of 4-cent and 7-cent stamps.
We now show our approach to this problem which will show that the recursive step only requires recursion (multiples) of 4-cent stamps and as mentioned above, the number of cases will be equal to the smaller of the two values, here 4.
BASE CASE:
| Postage | Combination |
|---|---|
| ¢ | |
| ¢ | |
| ¢ | |
| ¢ |
Thus, the first four values (base cases) can be obtained using 0 or more multiples of 4 and 7 cent stamps.
RECURSIVE STEP:
ANY number greater than 21 can be made by multiples of 4-cent stamps added to one of these four cases based on modular arithmetic:
| Base mod | |
|---|---|
Adding four or multiples of four to any of these mod functions will result in the SAME mod remainder:
| Generalized | |
|---|---|
Thus, there actually is only ONE variable that is recursing: namely, the number of 4-cent stamps. Consider any large number and keep subtracting 4 from it (each time subtracting 4 will be adding one 4-cent stamp) till one of 18-21 will be reached (the base cases). The appearance of two variables is only to set up the base case(s), which occur PRIOR to the first recursive step.
The postal stamp genre does not change the primitive recursion framework. The two denominations only serve to set up the multiple base cases, and having multiple base cases does not leave the rubric of primitive recursive. The whole point of these encodings is that, ultimately, you only need one base case and one previous level’s output - it is all based on simple induction and simple recursion.
Extended Recursion Schemes
Section titled “Extended Recursion Schemes”Mutual Recursion
Section titled “Mutual Recursion”Assume prior known primitive recursive functions G and H. Now define two mutually (primitive) recursive functions, E and F: E is defined recursively in terms of F, and similarly, F is defined in terms of E.
Base Case(s):
Assigned with constants, but also can be based on auxiliary functions.
Also, could have and .
Mutually Recursive Step:
‘s recursive step is not based on ‘s previous level’s output but on ‘s; likewise, ‘s recursive step is not based on ‘s previous level’s output but on ‘s.
Also, could have and .
Main part of proof that Mutual Recursion is Primitive Recursive by the Pairing Function encoding:
Consider : By plugging into where the previous level becomes :
G and H are known Primitive Recursive (as stated above) and now F is defined recursively in terms of F and no longer involves E. As such, we of course expect F to be proven to be Primitive Recursive. What is the difficulty? The recursive definition for F above is not stated in terms of previous level’s () output but rather the level before it (). This dependence on violates the primitive recursion format, which requires defining solely in terms of .
Resolve: Pairing Function comes to rescue here similar to the manner that it did for encoding Fibonacci.
Solution:
Base cases are , . The pairing function is simulating/encoding in general.
And then:
This proves that Mutual Recursion of primitive recursive functions is still primitive recursive. NOTE: For simplicity of presentation the base cases were assumed to involve constants and . In fact, they could involve functions, say, and as long as they are only used in defining the base cases. Then, the entire proof proceeds as above. It wasn’t done because there are so many functions floating around already. Adding two more would add more confusion. But if in fact, you wanted to, then:
but then and never appear again so the proof continues as is.
Simultaneous Recursion
Section titled “Simultaneous Recursion”Assume prior known primitive recursive functions G and H. Now define two simultaneously defined (primitive) recursive functions, E and F: E is defined recursively in terms of E and F, and similarly F is recursively defined in terms of E and F.
Base Case(s):
Assigned to auxiliary functions, but could also be constants.
Recursive Step:
‘s and ‘s recursive step each require the previous level output of both and . is an example of an auxiliary variable. This is by definition; i.e. that is how “simultaneous recursion” is defined.
Outline of proof that Simultaneous Recursion is Primitive Recursive by the Pairing Function encoding:
Pairing Function:
Left side defines and Right side defines ; encoding .
Finally:
Divide and Conquer
Section titled “Divide and Conquer”cf. Discrete Mathematics, Master Theorem aka Master Method; e.g. mergesort falls under this category.
Divide and conquer recursion differs from other instances of (primitive) recursion in that the decision of which previous level’s output you need to define the next level is based on a ratio of distance from the current level. For example:
Level is based on output at level . The above is termed Nonhomogeneous since it involves both a previous level output and a function of the level number (namely ).
This is clearly primitive recursive since course of values recursion keeps track of EVERY previous level’s output. See next tab for an elaboration on this.
Dynamic Programming
Section titled “Dynamic Programming”A misunderstood(?) recursion.
Dynamic programming (DP) is an algorithmic technique used to solve complex problems by breaking them down into simpler, overlapping subproblems and storing their solutions to avoid redundant computations (termed memoization).
Examples - three typical NP-Complete problems, proven after Karp’s paper:
- 0-1 Knapsack variant
- Subset Sum
- Partition
Read about these problems and their dynamic programming formulations. If there is time, we will discuss this more in another lecture.
Dynamic Programming typically will have an iterative nested double for-loops that will solve the problem. From a simplistic approach, the complexity would appear to be , since each for-loop is and they are nested. But, clearly the above NP-Complete problems exhibit exponential complexity if , which is assumed by most. The issue at hand is what does really mean. One of the for-loops in dynamic programming iterates through all possible output VALUES and not based on length of input. (We had a similar issue when we analyzed the nested triple for-loop that power (exponential) has when programming the nonrecursive coded version of the primitive function POWER (exponential) based on the successor function .)
On a separate note, a distinction should be made between the divide and conquer recursion and the dynamic programming recursion. Divide and conquer breaks up the problem on the original size (amount) of data in terms of a number of smaller pieces, but all of the same size. Dynamic programming also breaks up the problem of the original size amount of data in terms of a number of smaller pieces, but each of those pieces tend to be of different sizes. Finally, while solving discrete mathematics recurrence (or recursive) relations (or equations) and dynamic programming are all interested in the nonrecursive equivalent to obtain the exact solution, divide and conquer recursions “solved” by the Master Theorem (from discrete mathematics) only determine the order of complexity.