我正在通过SICP工作。在练习1.28中关于Miller-Rabin测试。我有这个代码,我知道这是错误的,因为它不遵循练习的内容。
(define (fast-prime? n times)
(define (even? x)
(= (remainder x 2) 0))
(define (miller-rabin-test n)
(try-it (+ 1 (random (- n 1)))))
(define (try-it a)
(= (expmod a (- n 1) n) 1))
(define (expmod base exp m)
(cond ((= exp 0) 1)
((even? exp)
(if (and (not (= exp (- m 1))) (= (remainder (square exp) m) 1))
0
(remainder (square (expmod base (/ exp 2) m)) m)))
(else
(remainder (* base (expmod base (- exp 1) m)) m))))
(cond ((= times 0) true)
((miller-rabin-test n) (fast-prime? n (- times 1)))
(else false)))
在其中我测试指数的平方是否与1 mod n一致。根据 我所读到的,以及我看到的其他正确实现是错误的。我应该测试一下 整个数字如:
...
(square
(trivial-test (expmod base (/ exp 2) m) m))
...
问题是我测试了这个,有许多素数和大的Carmicheal数, 它似乎给出了正确的答案,虽然有点慢。我不明白为什么这样 似乎工作。
答案 0 :(得分:3)
你的功能版本“有效”只是因为你很幸运。试试这个实验:评估(fast-prime? 561 3)
一百次。根据您的函数选择的随机证人,有时它将返回true,有时它将返回false。当我这样做时,我得到12真和88假,但你可能得到不同的结果,取决于你的随机数发生器。
> (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
我没有SICP在我面前 - 我的副本在家 - 但我可以告诉你正确的方法来进行Miller-Rabin素性测试。
您的expmod
功能不正确;没有理由对指数进行平方。这是执行模幂运算的正确函数:
(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)))))
然后是加里·米勒强大的假冒伪劣测试,这是你的try-it
测试的强大版本,其中有一个见证 a 证明了每个复合材料的复合性 n ,看起来像这样:
(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)))))))))
假设扩展黎曼假设,测试每个 a 从2到 n -1将证明(一个实际的,确定性证明,而不仅仅是对素数的概率估计)素数 n 的素数,或识别至少一个 a ,它是复合物 n 的复合性的见证。迈克尔·拉宾证明,如果 n 是复合的,那么 a 从2到 n -1的至少四分之三是该复合性的见证者,所以测试 k 随机基数证明了,但是没有证明,素数 n 的素数为4 -k 的概率。因此,这种米勒 - 拉宾素性测试的实现:
(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))))))
总是有效:
> (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
我知道你的目的是学习SICP而不是编写素数测试,但如果你对使用素数编程感兴趣,我谦虚地推荐this essay在我的博客上讨论Miller-Rabin测试,等主题。您还应该知道,随机Miller-Rabin有更好的(更快,更不可能报告错误结果)素性测试。
答案 1 :(得分:0)
在我看来,你仍然得到了正确答案,因为在expmod
的每次迭代中,你都会检查前一次迭代的条件。您可以尝试使用exp
内的display
函数调试expmod
值。实际上,您的代码与this one没有太大区别。