Complexity

Growth Rates

Assume f, g: NAT -> NAT are increasing functions (n <= m => f(n) <= f(m)).

We want to formalize the statement:

growth rate of f <= growth rate of g

Definition: g majorizes f, notation: f = O(g), if for some c & k: f(n) <= c * g(n) for k < n

Example 1:

100n2 + 1000 = O(n2) because 100n2 + 1000 <= 101n2 for n > sqrt(1000)

Also:

n2 = O(100n2 + 1000)

Example 2:

nk = O(nk+1)

Example 3:

nk = O(2n)

Example 4:

log2(n) = O(n)

Example 5:

n != O(1)

Theorem Assume f & g are increasing, then f = O(g) if and only limn->infinity(n)/g(n) < infinity

Example:

n3 != O(n2) because lim(n3/n2) = lim(3n2/2n) = lim(6n/2) = infinity

The hyper-exponential hierarchy

hyper0(n) = (n == 0)? 1: (n == 1)? 2: n + 2

hyperk+1(n) = hyperk(n)(1) = hyperk(hyperk(hyperk(...hyperk(1)..))) n-times

And so...

hyper1(n) = hyper0(hyper0(hyper0(...hyper0(1)..))) (n-times) = 2 + 2 + 2 + ... + 2 (n times) = 2 * n

hyper2(n) = hyper1(hyper1(hyper1(...hyper1(1)..))) = 2 * 2 * ... * 2 * 1 (n times) = 2n

hyper3(n) = 2^(2^(2^...(2^1)...)) n-times

Obviously, hyperk is a rapidly increasing function.

Theorem hyperk = O(hyperk+1) but hyperk + 1 != O(hyperk)

Definition ack(n) = hypern(n)

Theorem For each k hyperk = O(ack) but ack != O(hyperk)

The classes O(hyperk) form an expanding hierarchy among increasing functions in NAT->NAT:

Theorem

If p is any polynomial, then p = O(hyper2), but hyper2 != O(p)

Exercise: Implement hyperk in PL – {loop}

Exercise: Implement ack in PL

Note: This is hard and may require adding stacks or arrays to the PL VM

Algorithm Complexity

Assume P is a PL program that implements a function f: NAT -> NAT.

TP(n) = the number of instructions from P executed by the PL VM given input n

SP(n) = the number of variables used by P given input n

Definition The depth of a PL program = 1 if it contains no loop instructions, otherwise it's 1 + maximum depth of all loop bodies it contains.

Definition LOOP(n) = all LOOP programs with depth <= n. In other words, loops can only be nested <= n times

Theorem hypern can be implemented by a LOOP(n) program, but not a LOOP(n – 1) program

Proof

We show hypern can be implemented by a LOOP(n) program using induction.

Clearly hyper0(m) can be implemented in LOOP(0):

inc m
inc m

Let load a, hypern(m) be a macro that expands in to a LOOP(n) program by inductive assumption. Then hypern+1(m) is computed by

load r, 1
loop m
   load r, hypern(r)
end

To show that this can't be done in LOOP(n-1) follows from the next theorem.

QED.

Theorem If P is a LOOP(n) program, then TP = O(hypern)

Proof (sketch)

What we will show by induction on n is that there is a k such that for TP(V) <= hypern(k)(V) where V = max(vars)

Assume n = 0. But then P has no loops but then P has no loops so TP(V) = |P| = the number of instructions in P, which we take to be our k.

Assume the result is true for n, and P is a LOOP(n + 1) program of the form loop y P' end where P' is a LOOP(n) program.

So the first time P' is executed requires at most hypern(k)(V) steps.

Fact 1: For an program P, max(vars) <= TP(max(vars)) + max(vars) since a step can at most increment a variable by 1.

From this fact it follows that:

max(vars) <= hypern(k)(V) + V.

Fact 2: hypern(k)(V) + V <= hypern(k + 1)(V) for 1 <= n

From this fact we see that the next execution of P' requires at most hypern(k)( hypern(k+1)(V)) steps and from fact 1 we have:

max(vars) <= hypern(k)(hypern(k+1)(V)) + hypern(k+1)(V)

Fact 3: hypern(k)(x) + x <= hypern(k+1)(x) for 1 <= n

From this fact it follows that

max(vars) <= hypern(k)(hypern(k+1)(V)) + hypern(k+1)(V) <= hypern(k+1)(hypern(k+1)(V)) = hypern2(k+1)(V)

This is also a bound on the number of steps required for the first two iterations of P'.

The number of iterations of P' is <= V and so

TP(V) <= hypernV(k+1)(V) <= hypernV(k+2)(1) <= hypernW(1) where W = hypern+1(k+1)(V)

But:

hypernW(1) = hypern+1(hypern+1(k+1)(V)) <= 2 * hypern+1(k+2)(V) <= hypern+1(k+3)(V)

There are a few other cases, like when P is a sequence of loop P' end commands.

QED

Theorem If P is a PL – {goto} program, and TP = O(hypern) , then there is a LOOP(n) program Q such that P and Q implement the same function.

Theorem ack can't be implemented in PL – {goto}

Theorem f is primitive recursive if and only if f can be implemented in LOOP.

Proof: We haven't defined primitive recursive, so this can be taken as a definition.