Your version of the function "works" only because you are lucky. Try this experiment: evaluate (fast-prime? 561 3)
a hundred times. Depending on the random witnesses that your function chooses, sometimes it will return true and sometimes it will return false. When I did that I got 12 true and 88 false, but you may get different results, depending on your random number generator.
> (let loop ((k 0) (t 0) (f 0))
(if (= k 100) (values t f)
(if (fast-prime? 561 3)
(loop (+ k 1) (+ t 1) f)
(loop (+ k 1) t (+ f 1)))))
12
88
I don't have SICP in front of me -- my copy is at home -- but I can tell you the right way to perform a Miller-Rabin primality test.
Your expmod
function is incorrect; there is no reason to square the exponent. Here is a proper function to perform modular exponentiation:
(define (expm b e m) ; modular exponentiation
(let loop ((b b) (e e) (x 1))
(if (zero? e) x
(loop (modulo (* b b) m) (quotient e 2)
(if (odd? e) (modulo (* b x) m) x)))))
Then Gary Miller's strong pseudoprime test, which is a strong version of your try-it
test for which there is a witness a that proves the compositeness of every composite n, looks like this:
(define (strong-pseudoprime? n a) ; strong pseudoprime base a
(let loop ((r 0) (s (- n 1)))
(if (even? s) (loop (+ r 1) (/ s 2))
(if (= (expm a s n) 1) #t
(let loop ((r r) (s s))
(cond ((zero? r) #f)
((= (expm a s n) (- n 1)) #t)
(else (loop (- r 1) (* s 2)))))))))
Assuming the Extended Riemann Hypothesis, testing every a from 2 to n-1 will prove (an actual, deterministic proof, not just a probabilistic estimate of primality) the primality of a prime n, or identify at least one a that is a witness to the compositeness of a composite n. Michael Rabin proved that if n is composite, at least three-quarters of the a from 2 to n-1 are witnesses to that compositeness, so testing k random bases demonstrates, but does not prove, the primality of a prime n to a probability of 4−k. Thus, this implementation of the Miller-Rabin primality test:
(define (prime? n k)
(let loop ((k k))
(cond ((zero? k) #t)
((not (strong-pseudoprime? n (random (+ 2 (- n 3))))) #f)
(else (loop (- k 1))))))
That always works properly:
> (let loop ((k 0) (t 0) (f 0))
(if (= k 100) (values t f)
(if (prime? 561 3)
(loop (+ k 1) (+ t 1) f)
(loop (+ k 1) t (+ f 1)))))
0
100
I know your purpose is to study SICP rather than to program primality tests, but if you're interested in programming with prime numbers, I modestly recommend this essay at my blog, which discusses the Miller-Rabin test, among other topics. You should also know there are better (faster, less likely to report erroneous result) primality tests available than randomized Miller-Rabin.