my attempt to do the exercises in sicp.

Saturday, July 5, 2008

sicp exercise 1.28 *

;  Exercise 1.28. One variant of the Fermat test that cannot be fooled is called the Miller-Rabin test
;  (Miller 1976; Rabin 1980). This starts from an alternate form of Fermat's Little Theorem, which states
;  that if n is a prime number and a is any positive integer less than n, then a raised to the (n - 1)st
;  power is congruent to 1 modulo n. To test the primality of a number n by the Miller-Rabin test, we
;  pick a random number a<n and raise a to the (n - 1)st power modulo n using the expmod procedure.
;  However, whenever we perform the squaring step in expmod, we check to see if we have discovered a
;  ``nontrivial square root of 1 modulo n,'' that is, a number not equal to 1 or n - 1 whose square is
;  equal to 1 modulo n. It is possible to prove that if such a nontrivial square root of 1 exists, then
;  n is not prime. It is also possible to prove that if n is an odd number that is not prime, then, for
;  at least half the numbers a<n, computing an-1 in this way will reveal a nontrivial square root of
;  1 modulo n. (This is why the Miller-Rabin test cannot be fooled.) Modify the expmod procedure to
;  signal if it discovers a nontrivial square root of 1, and use this to implement the Miller-Rabin test
;  with a procedure analogous to fermat-test. Check your procedure by testing various known primes and
;  non-primes. Hint: One convenient way to make expmod signal is to have it return 0.

(define (carmichael index)
   (cond ((= index 1)  561)
         ((= index 2) 1105)
         ((= index 3) 1729)
         ((= index 4) 2465)
         ((= index 5) 2821)
         ((= index 6) 6601)
         (else 0)))

; to test if num is nontrivial square root of 1 mod n
(define (non-trivial-sq-root? num n)
    (cond ((= num (- n 1)) #f)
          ((= num 1) #f)
          (else (= (square num) (remainder 1 n)))))

(define (square num) (* num num))

(define (expmod base exp m)
    (cond ((= exp 0) 1)
          ((even? exp)
               (if (non-trivial-sq-root? exp m) 0 (remainder (square (expmod base (/ exp 2) m)) m)))
               (remainder (* base (expmod base (- exp 1) m)) m))))

(define (check res base) (and (not (= res 0)) (= res base)))

(define (prime? n)
    (define (prime-impl n base)
        (check (expmod base (- n 1) n) base))
   (prime-impl n (random (- n 1))))

(display (prime? 23)) (newline)
(display (prime? 24)) (newline)
(display (prime? (carmichael 1))) (newline)
(display (prime? (carmichael 2))) (newline)
(display (prime? (carmichael 3))) (newline)
(display (prime? (carmichael 4))) (newline)
(display (prime? (carmichael 5))) (newline)
(display (prime? (carmichael 6))) (newline)

No comments: