miller-rabin-composite? rewrite (issue #751)

modular-root-of-one? is replaced with the correct witness tester
This commit is contained in:
Roger Crew 2021-06-27 03:13:06 -07:00
parent 7d39108e72
commit 41aa1a918e
2 changed files with 41 additions and 24 deletions

View file

@ -107,4 +107,7 @@
5772301760555853353 5772301760555853353
(* 2936546443 3213384203))) (* 2936546443 3213384203)))
(test "Miller-Rabin vs. Carmichael prime"
#t (miller-rabin-composite? 118901521))
(test-end)))) (test-end))))

View file

@ -74,34 +74,48 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; Probable primes. ;; Probable primes.
(define (modular-root-of-one? twos odd a n neg1) ;; Given \var{n}, return a predicate that tests whether
;; Returns true iff any (modular-expt a odd*2^i n) for i=0..twos-1 ;; its argument \var{a} is a witness for \var{n} not being prime,
;; returns 1 modulo n. ;; either (1) because \var{a}^(\var{n}-1)≠1 mod \var{n}
(let ((b (modular-expt a odd n))) ;; \em{or} (2) because \var{a}'s powers include
(let lp ((i 0) (b b)) ;; a third square root of 1 beyond {1, -1}
(cond ((or (= b 1) (= b neg1))) ; in (= b 1) case we could factor (define (miller-rabin-witnesser n)
((>= i twos) #f)
(else (lp (+ i 1) (remainder (* b b) n)))))))
;;> Returns true if we can show \var{n} to be composite by finding an
;;> exception to the Miller Rabin lemma.
(define (miller-rabin-composite? n)
(let ((neg1 (- n 1))) (let ((neg1 (- n 1)))
(factor-twos neg1 (factor-twos neg1
(lambda (twos odd) (lambda (twos odd)
;; Each iteration of Miller Rabin reduces the odds by 1/4, so (lambda (a)
;; this is a 1 in 2^40 probability of false positive, (let ((b (modular-expt a odd n)))
;; assuming good randomness from SRFI 27 and no bugs, further (let lp ((i 0) (b b))
;; reduced by preliminary sieving. (cond ((= b neg1)
(let* ((fixed-limit 16) ;; found -1 (expected sqrt(1))
(rand-limit (if (< n 341550071728321) fixed-limit 20))) #f)
(let try ((i 0)) ((= b 1)
(and (< i rand-limit) ;; !! (previous b)^2=1 and was not 1 or -1
(let ((a (if (< i fixed-limit) (not (zero? i)))
((>= i twos)
;; !! a^(n-1)!=1 mod n
)
(else
(lp (+ i 1) (remainder (* b b) n)))))))))))
;;> Returns true if we can show \var{n} to be composite
;;> using the Miller-Rabin test (i.e., finding a witness \var{a}
;;> where \var{a}^(\var{n}-1)≠1 mod \var{n} or \var{a} reveals
;;> the existence of a 3rd square root of 1 in \b{Z}/(n))
(define (miller-rabin-composite? n)
(let* ((witness? (miller-rabin-witnesser n))
;; Each iteration of Miller Rabin reduces the odds by 1/4, so
;; this is a 1 in 2^40 probability of false positive,
;; assuming good randomness from SRFI 27 and no bugs, further
;; reduced by preliminary sieving.
(fixed-limit 16)
(rand-limit (if (< n 341550071728321) fixed-limit 20)))
(let try ((i 0))
(and (< i rand-limit)
(or (witness? (if (< i fixed-limit)
(vector-ref prime-table i) (vector-ref prime-table i)
(+ (random-integer (- n 3)) 2)))) (+ (random-integer (- n 3)) 2)))
(or (not (modular-root-of-one? twos odd a n neg1)) (try (+ i 1)))))))
(try (+ i 1)))))))))))
;;> Returns true if \var{n} has a very high probability (enough that ;;> Returns true if \var{n} has a very high probability (enough that
;;> you can assume a false positive will never occur in your lifetime) ;;> you can assume a false positive will never occur in your lifetime)