The following procedure takes any positive integer as argument and returns a list of its prime factors.
;;; prime-factors: construct and return a list of the ;;; prime factors of a given positive integer ;;; Given: ;;; NUMBER, an exact integer. ;;; Result: ;;; FACTORS, a list of exact integers. ;;; Precondition: ;;; NUMBER is positive. ;;; Postconditions: ;;; (1) Every element of FACTORS is a prime natural number. ;;; (2) The product of the elements of FACTORS is NUMBER. (define prime-factors (lambda (number) (if (not (and (integer? number) (exact? number) (positive? number))) (error "prime-factors: requires an exact positive integer") (prime-factors-kernel number 2)))) ;;; prime-factors-kernel: construct and return a list of the ;;; prime factors of a given positive integer that are greater ;;; than or equal to another given positive integer ;;; Given: ;;; NUMBER and DIVISOR, both exact integers. ;;; Result: ;;; FACTORS, a list of exact integers. ;;; Preconditions: ;;; (1) NUMBER is positive. ;;; (2) DIVISOR is greater than or equal to 2. ;;; (3) NUMBER is not divisible by any positive integer ;;; greater than or equal to 2 but less than DIVISOR. ;;; Postconditions: ;;; (1) Every element of FACTORS is a prime natural number ;;; greater than or equal to DIVISOR. ;;; (2) The product of the elements of FACTORS is NUMBER. (define prime-factors-kernel (lambda (number divisor) (cond ((= number 1) '()) ((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 '(). Since the arguments in (=
number 1) can be evaluated immediately, and the = procedure always
terminates, and '() 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.