SICP 全笔记

Exercise 1.27. Demonstrate that the Carmichael numbers listed in footnote 47 really do fool the Fermat test. That is, write a procedure that takes an integer n and tests whether an is congruent to a modulo n for every a<n, and try your procedure on the given Carmichael numbers.

我们需要把每一个小于 n 的数字当做参数进行对 n 的 fermat 测试。这可以通过如下过程实现:

(define (expmod base exp m)
  (cond ((= exp 0) 1)
        ((even? exp)
         (remainder (square (expmod base (/ exp 2) m))
                    m))
        (else
         (remainder (* base (expmod base (- exp 1) m))
                    m))))

(define (test-fermat n)
  (define (is-fermat-fine? a)
    (= a (expmod a n n)))
  (define (test-each-num-below x)
    (cond ((= x 2) #t)
          ((is-fermat-fine? x)
           (test-each-num-below (- x 1)))
          (else #f)))
  (test-each-num-below (- n 1)))

测试的过程则需要对 Carmichael 数字进行两遍测试,使用我们的过程是会把 Carmichael 数字当做素数的,但使用以前的 prime? 则不会。


(load "../testframe.scm")
;; test-fermat will return #t if n is decided being prime using fermat method
;; #f otherwise
(asserteq? #t (test-fermat 199))
(asserteq? #t (test-fermat 1999))
(asserteq? #f (test-fermat 19999))

;; carmichael number!
(asserteq? #t (test-fermat 561))
(asserteq? #t (test-fermat 1105))
(asserteq? #t (test-fermat 1729))
(asserteq? #t (test-fermat 2465))
(asserteq? #t (test-fermat 2821))
(asserteq? #t (test-fermat 6601))

;; but the prime? procedure can decide the primality
(define (find-divisor n test-divisor)
  (define (divides? a b)
  (= (remainder b a) 0))  
  (define (next num)
    (cond ((even? num) (+ num 1))
          (else (+ num 2))))
  (cond ((> (square test-divisor) n) n)
        ((divides? test-divisor n) test-divisor)
        (else (find-divisor n (next test-divisor)))))
(define (prime? n)
  (define (smallest-divisor n)
    (find-divisor n 2))  
  (= n (smallest-divisor n)))

(asserteq? #f (prime? 561))
(asserteq? #f (prime? 1105))
(asserteq? #f (prime? 1729))
(asserteq? #f (prime? 2465))
(asserteq? #f (prime? 2821))
(asserteq? #f (prime? 6601))