How Fermat Factored Integers

July 18, 2014

We begin by calculating the table of squares. Instead of printing it as we did on the previous page, we store it in a vector, plus one additional square to act as a sentinal:

(define limit 10000)

(define squares
  (let ((squares (make-vector (+ limit 1))))
    (do ((i 0 (+ i 1))) ((< limit i) squares)
      (vector-set! squares i (* i i)))))

Then isqrt, which calculates the largest number that, when multiplied by itself, doesn’t exceed n, and in-table, which returns the square root of n if n is a square or #f otherwise, are simple:

(define (isqrt n)
  (do ((i 1 (+ i 1))) ((< n (vector-ref squares i)) (- i 1)))))

(define (in-table n)
  (let loop ((i 0))
    (cond ((< n (vector-ref squares i)) #f)
          ((= n (vector-ref squares i)) i)
          (else (loop (+ i 1))))))

We calculate the digital root algorithmically; Fermat would certainly have calculated it visually by casting out nines:

(define (digital-root n)
  (let loop ((n n) (r 0))
    (cond ((zero? n) (if (< r 10) r (digital-root r)))
          ((< n 10) (loop 0 (+ r n)))
          (else (let ((d (modulo n 10)))
                  (loop (/ (- n d) 10) (+ r d)))))))

Here we encode the rules for identifying squares. The square? function returns the square root if it exists or #f otherwise:

(define (square? n)
  (let* ((ones (modulo n 10))
         (tens (modulo (/ (- n ones) 10) 10)))
    (and (or (and (= 0 ones) (zero? tens))
             (and (= 1 ones) (even? tens))
             (and (= 4 ones) (even? tens))
             (and (= 5 ones) (= 2 tens))
             (and (= 6 ones) (odd? tens))
             (and (= 9 ones) (even? tens)))
         (member (digital-root n) '(1 4 7 9))
         (in-table n))))

We finish with the function that simulates Fermat’s calculation. First we ensure n fits in our table and remove factors of 2. Then we initialize r and t, iterate until we find a square, and calculate the factors:

(define (fermat n)
  (if (not (< -1 n (* limit limit))) #f
    (if (even? n) (list 2 (/ n 2))
      (let ((x (isqrt n)))
        (if (= (* x x) n) (list x x)
          (let loop ((r (- (* x x) n)) (t (+ x x 1)))
            (if (not (square? r))
                (loop (+ r t) (+ t 2))
                (let ((x (/ (- t 1) 2)) (y (isqrt r)))
                  (list (- x y) (+ x y))))))))))

You can run the program at http://programmingpraxis.codepad.org/NtvkBMaG:

About these ads

Pages: 1 2 3

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 629 other followers

%d bloggers like this: