The following procedure takes any positive integer as argument and returns a list of its prime factors.
(define prime-factors
(lambda (number)
(prime-factors-kernel number 2)))
(define prime-factors-kernel
(lambda (number divisor)
(cond ((= number 1) null)
((zero? (remainder number divisor))
(cons divisor
(prime-factors-kernel (quotient number divisor)
divisor)))
(else (prime-factors-kernel number (+ divisor 1))))))
We want to prove that any invocation of prime-factors
eventually terminates. Since the body of the definition of
prime-factors consists entirely of a call to the
prime-factors-kernel procedure with arguments that can be
evaluated immediately, the invocation of prime-factors
terminates if, and only if, this initial invocation of
prime-factors-kernel terminates.
number is 1, the invocation of
prime-factors-kernel terminates.
Suppose that the value of number is 1. Then the only steps
in
the evaluation of the body of prime-factors-kernel are the
evaluation of (= number 1) and of null. Since
the arguments in (= number 1) can be evaluated immediately,
and the = procedure always terminates, and null
can be evaluated immediately, the invocation of
prime-factors-kernel terminates in this case. No recursive
calls are needed.
number and divisor
are equal (and greater than 1), the invocation of
prime-factors-kernel terminates.
Suppose that the values of number and divisor
are equal (and greater than 1). Then the only steps in
the evaluation of the body of prime-factors-kernel are the
evaluation of (= number 1), of (zero? (remainder number
divisor)), and of (cons divisor (prime-factors-kernel
(quotient number divisor) divisor)). Number and
divisor can be evaluated immediately, and the
remainder, zero?, and quotient
procedures always terminate (without error, since divisor is
known to be non-zero), so the only possible problem is the evaluation of
the recursive procedure call. However, since number and
divisor are equal, (quotient number divisor) is
1, so in the recursive procedure call the first argument is 1. Hence, by
step 1, it will terminate. Hence the original invocation of
prime-factors-kernel terminates in this case as well.
prime-factors-kernel,
either the value of number is 1, or the values of
number and divisor are equal, or the value of
number is greater than the value of divisor; and
in addition no integer greater than 1 and less than the value of
divisor evenly divides number.
First, consider the original call to
prime-factors-kernel -- the one in the body of the definition
of prime-factors. The first argument must be either less
than, equal to, or greater than the second. But the second argument is 2;
so the first argument is either less than 2, or greater than or equal to
the second argument. But the first argument is, by precondition, a
positive integer, and the only positive integer less than 2 is 1. So the
first argument is either 1, or else equal to or greater than the second
argument.
Moreover, there are no integers greater than 1 and less than 2, so obviously no such integer divides the first argument.
Next, suppose that we have started an evaluation of the body of
prime-factors-kernel with the knowledge that either the value
of number is 1, or the values of number and
divisor are equal, or the value of number is
greater than the value of divisor; and in addition no integer
greater than 1 and less than the value of divisor evenly
divides number.
Consider, then the recursive call to prime-factors-kernel in
the second cond-clause in the definition of that procedure.
If we reach that call, the value of number cannot be 1, since
then we would have selected the action in the first
cond-clause. So the value of number is greater
than or equal to the value of divisor. If the two values are
equal, then in the recursive call the first argument, the value of
(quotient number divisor), is 1. On the other hand, if the
value of number is greater than the value of
divisor, then the value of (quotient number
divisor) is greater than the value of divisor, since
otherwise the value of (quotient number divisor) would be an
integer greater than 1 and less than divisor that evenly
divides the value of number, contrary to hypothesis. So in
the recursive invocation either the first argument is 1 or it is equal to
or greater than the second argument.
Moreover, any exact divisor of the first argument (the value of
(quotient number divisor), remember) is also a divisor of
number. Since no integer greater than 1 and less than
divisor evenly divides the value of number, no
such integer evenly divides the first argument in the recursive invocation
of prime-factors-kernel either.
Next, consider the recursive invocation of
prime-factors-kernel in the third cond-clause.
If we reach that call, we know that the value of number is
strictly greater than the value of divisor, since if the
value
of number were 1, we would have selected the action in the
first cond-clause, and if the values of number
and divisor were equal, we would have selected the action in
the second cond-clause. In the recursive application, the
second argument is one greater than the value of divisor; but
this is still less than or equal to the first argument, since
the value of number is strictly greater than the value of
divisor.
Also, in the recursive application, no integer
greater than 1 and less than divisor evenly divides the value
of number, by hypothesis; and the value of
divisor itself does not evenly divide the value of
number (since if it did, we would have selected the action in
the second cond-clause and never reached the third one). So
no integer greater than 1 and less than the value of (+ divisor
1) evenly divides the value of number. In other
words,
in the recursive invocation, no number greater than 1 and less than the
second argument evenly divides the first argument.
Now, since the original invocation of prime-factors-kernel
meets the complicated condition stated in the header for this step, and
since, as we have proven, any recursive invocation meets the same
condition
if undertaken as part of an invocation that itself meets that condition,
every invocation of prime-factors-kernel meets that
condition.
prime-factors-kernel
that meets the condition stated in step 3 terminates.
Let d be the result of subtracting the value of
divisor
from the value of number. We know from step 3 that either
the value of number is 1, in which case the invocation
terminates as shown in step 1, or d is zero, in which case the
invocation terminates as shown in step 2, or d is positive.
Suppose, then, that any invocation in which d is less than or equal
to some natural number k terminates, and consider a case in which
d is k + 1. Now, either the value of divisor
evenly divides the value of number or it does not.
If it does, then the only steps in the evaluation of the body of the
definition of prime-factors-kernel are the evaluation of
(= number 1), of (zero? (remainder number
divisor)), and of (cons divisor (prime-factors-kernel
(quotient number divisor) divisor)). As in step 2, all of these
expressions obviously terminate except the recursive call. But in that
recursive call the result of subtracting the second argument from the
first
is less than d, that is, it is k or less. So, by
hypothesis,
this recursive call also terminates.
On the other hand, if divisor does not evenly divide number,
then the steps in the evaluation of the body of the definition of
prime-factors-kernel are the evaluation of (= number
1), of (zero? (remainder number divisor)), and of
(prime-factors-kernel number (+ divisor 1)). Again, as in
step 2, only the recursive call is a problem. But in the recursive call
the difference between the first and second arguments is one less than
d -- that is, it is k. So, again by hypothesis, the
recursive call also terminates.
Thus we have shown that the proposition holds whenever d is
negative
or zero and also for any positive integer such that it holds for all
lesser
natural numbers. But this means that it holds for any integer value
d, by mathematical induction. So every invocation of
prime-factors-kernel that meets the condition stated in step
3
terminates.
prime-factors-kernel
terminates.
Since every invocation of prime-factors-kernel that meets the
condition stated in step 3 terminates, and since every invocation
of prime-factors-kernel meets that condition, every
invocation
of prime-factors-kernel terminates.
This document is available on the World Wide Web as
http://www.cs.grinnell.edu/~gum/courses/151/readings/prime-factors-proof.xhtml
created February 21, 2000
last revised August 11, 2001
John David Stone (stone@cs.grinnell.edu) and Ben Gum (gum@cs.grinnell.edu)