FACTORING INTEGERS

As a warmup to obtaining full factorizations, let's look at a straightforward primality test. We'll assume that the argument to the predicate prime? is an integer nnot less than 2. If n is even, it's prime if and only if it is 2; otherwise, we can try out odd divisors until we either (a) exceed the square root of n or (b) obtain a remainder of zero:

(define prime?
  (lambda (n)
    (if (even? n)
        (= n 2)
        (let loop ((trial-divisor 3))
          (or (< n (* trial-divisor trial-divisor)) 
              (and (not (zero? (remainder n trial-divisor)))
                   (loop (+ trial-divisor 2))))))))
Now for the full problem: Given a positive integer, construct a (monotonically nondecreasing) list of its prime factors. It will be convenient to work through the potential divisors in ascending order, just as in prime?, except that now we need to keep any divisors that we find on a list (and simultaneously to divide them out of the number we're factoring). Just as in prime?, it's useful to handle 2 as a special case -- essentially, pre-processing the number by dividing out all the twos before entering the main loop.

Both the elimination of the twos and the main loop are complicated enough to be separated off as locally defined procedures, here called extract-twos and extract-odd-factors; the factor procedure itself simply calls extract-twos, checks to see whether the residual quotient is 1 (in which case the factorization is complete), and, if not, calls extract-odd-factors to finish the job.

(define factor
  (let ((extract-twos
          (lambda (n)
            (let loop ((two-list '())
                       (rest n))
              (if (even? rest)
                  (loop (cons 2 two-list) (quotient rest 2))
                  (cons rest two-list)))))

        (extract-odd-factors
          (lambda (partial-factorization)
            (let loop ((so-far (cdr partial-factorization))
                       (odd-product (car partial-factorization))
                       (trial-divisor 3))
              (cond ((< odd-product (* trial-divisor trial-divisor))
                     (reverse (cons odd-product so-far)))
                    ((zero? (remainder odd-product trial-divisor))
                     (loop (cons trial-divisor so-far)
                           (quotient odd-product trial-divisor)
                            trial-divisor))
                    (else
                     (loop so-far
                           odd-product
                           (+ trial-divisor 2))))))))

    (lambda (n)
      (let ((partial-factorization (extract-twos n)))
        (if (= (car partial-factorization) 1)
            (cdr partial-factorization)
            (extract-odd-factors partial-factorization))))))
The extract-twos procedure has to return, in effect, two results -- the residual quotient (the odd number that is left when all of the twos have been divided out of the original integer) and the list of factors so far accumulated (all equal to 2, of course). The conventional way for a procedure to return two values is to return a pair with one value as its car and the other as its cdr, and that is the approach used here. The resulting pair can be thought of as a ``partial factorization'' -- for instance, (extract-twos 180) returns (45 . (2 2)), which can also be written as (45 2 2).

The extract-odd-factors procedure is a simple adaptation of the main loop of prime?, accumulating any divisors that it finds and continuing until all possible divisors have been identified. Note that the value of trial-divisor cannot increase unless the procedure has established that it does not divide the value of odd-product, since it might be necessary to divide out the same odd prime more than once.

The two sub-procedures should be local to factor, but it is preferable to construct them only once, when factor is defined, rather than every time factor is called. Placing them in a let-expression that encloses the lambda-expression achieves this goal.


This document is available on the World Wide Web as

http://www.math.grin.edu/~stone/events/scheme-workshop/factoring.html


created July 10, 1995
last revised June 21, 1996

John David Stone (stone@math.grin.edu)