当代码似乎错误时,Scheme中的Miller-Rabin过程为什么工作?

当代码似乎错误时,Scheme中的Miller-Rabin过程为什么工作?,scheme,lisp,sicp,Scheme,Lisp,Sicp,我通过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))

我通过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模n一致。根据 我所读到的和我所看到的其他正确的实现都是错误的。我应该测试一下 完整的数字如下所示:

...
        (square 
         (trivial-test (expmod base (/ exp 2) m) m))
...
问题是我已经用很多素数和大卡米切尔数测试过了, 它似乎给出了正确的答案,尽管速度慢了一点。我不明白为什么会这样
似乎有效

在我看来,您仍然得到了正确的答案,因为在
expmod
的每次迭代中,您都会检查上一次迭代的条件。您可以尝试使用
expmod
中的
display
函数调试
exp
值。事实上,您的代码与之没有太大区别。

您的函数版本“有效”只是因为您运气好。尝试这个实验:评估
(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——我的副本在家里——但我可以告诉你进行米勒-拉宾素性测试的正确方法

您的
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)))))
然后Gary Miller的强伪素数测试,这是您的
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)))))))))
假设扩展Riemann假设,从2到n-1的每一个a测试将证明(实际的、确定性的证明,而不仅仅是素数的概率估计)素数n的素数,或者至少确定一个a是复合n的复合性的见证。迈克尔·拉宾证明了若n是复合的,从2到n-1的a中至少有四分之三是复合性的见证人,所以测试k个随机基证明了,但并没有证明,素数n的素数概率为4−K因此,Miller-Rabin素性测试的实现:

(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而不是编写素数测试,但如果你对素数编程感兴趣,我会在我的博客上谦虚地推荐你,该博客讨论了Miller-Rabin测试以及其他主题。你也应该知道,有比随机Miller Rabin更好的(更快,更不可能报告错误的结果)素性测试可用。

你所说的
琐碎测试是什么意思?
?我知道我的代码是错误的,但被愚弄了,因为当你尝试许多
时间时,它会给出“好”的结果。每一个素数有10个(这是我在提问前测试时使用的最小值),它在1000个素数中只给出3个不正确的结果,这解释了为什么我“总是”得到正确的答案。样本量是我的问题。我被随机性愚弄了-P谢谢你的回答,下次我会记得用大样本测试任何概率算法。谢谢你的解释。SICP中的练习未能清楚地传达这样一个事实(对于我们,非数学家!),即米勒的测试仍然是一个概率测试:)。因此,在正确地解决了这个问题后,我也在为重复测试运行和长测试序列的古怪答案而挣扎:)