Ackermann's Function


Ackermann' function was conceived in 1928 as a function that cannot be computed with first-order primitive recursion. It has type nat->nat->nat and is defined with three equations.

(* 
            ack 0 n = S n
        ack (S m) 0 = ack m 1
    ack (S m) (S n) = ack m (ack (S m) n)
*)


The equations define a function since they are exhaustive and terminating (either the first argument is decreased, or the first argument stays the same and the second argument is decreased). However, Coq will not accept this definition since it is not by structural recursion. But we can still use the equations to specify Ackermann's function.

Definition ackermann f := forall m n,
f 0 n = S n /\
f (S m) 0 = f m 1 /\
f (S m) (S n) = f m (f (S m) n).

Proposition Equivalence: forall f g,
ackermann f -> ackermann g ->
forall m n, f m n = g m n.

Proof. intros f g F G.
induction m.
intro n. elim (F 0 n). elim (G 0 n). intuition. congruence.
induction n.
elim (F m 0). elim (G m 0). intuition. rewrite IHm in * |-. congruence.
elim (F m n). elim (G m n). intuition. rewrite IHm in * |-. congruence.
Qed.

Coq no knows when a procedure computes Ackermann's function. But we still have to construct such a procedure. To do, we take the specify equations and write a recursive procedure acker that takes the first argument and returns a recursive procedure that takes the second argument.

Fixpoint acker m := match m with
| O => S
| S m => (fix acker' f n := match n with
    | O => f 1
    | S n => f (acker' f n)
    end)
   (acker m)
end.

Since both recursions are structural, Coq accepts this definition. The proof that acker computes Ackermann's function is straightforward.

Goal ackermann acker.
unfold ackermann. simpl. auto.

We now unfold the nested recursion into a stand-alone procedure.

Fixpoint ack' f n := match n with
| 0 => f 1
| S n => f(ack' f n)
end.

Fixpoint ack m := match m with
| 0 => S
| S m => ack' (ack m)
end.

Note that the auxiliary procedure ack' is higher-order (i.e., takes a procedure as argument.) The correctness proof is again straightforward.

Goal ackermann ack.
unfold ackermann. simpl. auto.

We now formulate primitive recursion in Coq by means of the function iter.

Fixpoint iter X (f:X->X) n x := match n with
  O => x
| S n' => f(iter X f n' x)
end.

We will show that we can compute Ackermann with iter (i.e., primitive recursion) and without further structural recursion. We first show how iter can compute ack and ack'.

Goal forall f n, ack' f n = iter nat f n (f 1).
induction n. reflexivity. simpl. rewrite IHn. reflexivity.

Goal forall n, ack n = iter (nat->nat) ack' n S.
induction n. reflexivity. simpl. rewrite IHn. reflexivity.

By inlining the iter formulation of ack' into the iter formulation of ack we obtain a pure iter formulation of the Ackermann function.

Goal ackermann (fun m => iter (nat->nat) (fun f n => iter nat f n (f 1)) m S).
unfold ackermann. simpl. auto.

We now know that Ackermann's function can be computed with higher-order primitive recursion (the outer iter is at nat->nat).

This page has been generated by coqdoc