SICP练习1.28-Miller-Rabin-“至少一半的数字将显示1模n的平凡平方根”

时间:2019-05-03 11:52:44

标签: scheme primes sicp primality-test

SICP练习1.28

https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-11.html#%_thm_1.28

  

费马测试的一个变种,不能被愚弄   称为Miller-Rabin检验(Miller 1976; Rabin 1980)。这开始   来自费马小定理的另一种形式,其中指出   如果n是素数,并且a是小于n的任何正整数,则   提升到第(n-1)次幂的a等于1模n。去测试   由Miller-Rabin检验得出的n的素数,我们选择一个随机数   数a 如果n是不是质数的奇数,则,   对于至少一半的数字a (这就是为什么   无法欺骗Miller-Rabin测试。)将expmod过程修改为   如果发现非平凡平方根为1则发出信号,并使用它来   使用类似于以下步骤的程序执行Miller-Rabin测试   费马测试。通过测试各种已知的质数来检查您的过程   非素数。提示:产生expmod信号的一种简便方法是   返回0。

我已经编写了自己的解决方案,其结果与此处提供的解决方案一致:

http://community.schemewiki.org/?sicp-ex-1.28

15是一个不是质数的奇数,因此对于a1的数字14的至少一半,我希望计算expmod(a, 14, 15)将显示1模n的非平凡平方根,由expmod返回0表示。

但是,这些是我得到的结果:

(expmod 1 14 15)
> 1
(expmod 2 14 15)
> 4
(expmod 3 14 15)
> 9
(expmod 4 14 15)
> 0
(expmod 5 14 15)
> 10
(expmod 6 14 15)
> 6
(expmod 7 14 15)
> 4
(expmod 8 14 15)
> 4
(expmod 9 14 15)
> 6
(expmod 10 14 15)
> 10
(expmod 11 14 15)
> 0
(expmod 12 14 15)
> 9
(expmod 13 14 15)
> 4
(expmod 14 14 15)
> 1

可以看出,这些结果中只有2个是0,比预期的至少少7个。

我是否误解了该声明?我是个白痴吗?代码是否错误? SICP错误吗?非常感谢。

编辑1:有人要求我提供我正在使用的确切代码。就是这里,尽管我实际上只是复制链接到的解决方案,并将remainder别名为mod,因为这是我的解释器所称的。

 (define (square x) (* x x))

 (define remainder mod)

 (define (miller-rabin-expmod base exp m) 
   (define (squaremod-with-check x) 
     (define (check-nontrivial-sqrt1 x square) 
       (if (and (= square 1) 
                (not (= x 1)) 
                (not (= x (- m 1)))) 
           0 
           square)) 
     (check-nontrivial-sqrt1 x (remainder (square x) m))) 
   (cond ((= exp 0) 1) 
         ((even? exp) (squaremod-with-check 
                       (miller-rabin-expmod base (/ exp 2) m))) 
         (else 
          (remainder (* base (miller-rabin-expmod base (- exp 1) m)) 
                     m))))

(define expmod miller-rabin-expmod)

(print (expmod 1 14 15))
(print (expmod 2 14 15))
(print (expmod 3 14 15))
(print (expmod 4 14 15))
(print (expmod 5 14 15))
(print (expmod 6 14 15))
(print (expmod 7 14 15))
(print (expmod 8 14 15))
(print (expmod 9 14 15))
(print (expmod 10 14 15))
(print (expmod 11 14 15))
(print (expmod 12 14 15))
(print (expmod 13 14 15))
(print (expmod 14 14 15))

编辑2:我现在还手动计算了expmod(a, 14, 15)的步骤(这些步骤总是通过exp = 14exp = 7exp = 6exp = 3,{ {1}},exp = 2exp = 1),对于exp = 0从1到14的所有值,我确定只有aa = 4遇到一个不平凡的平方根1。因此,我倾向于认为SICP在这方面有误,或者表达不明确。

4 个答案:

答案 0 :(得分:2)

SICP是错误的,因为它使用了Miller–Rabin证人的错误定义(请参见Keith Conrad,The Miller–Rabin Test)。特别是,以下行是错误的:

错误的说法。 也有可能证明,如果n是不是质数的奇数,那么至少对a

您可以验证其为假,例如n = 9时。

按照上述参考中的定理2.9,正确的陈述应为:

正确的声明。 让n> 1为非质数。然后我们可以将n-1记为(2 ^ e)k,使得e≥1并且k为奇数。 (例如,如果n = 21,我们可以写成21-1 = 20 =(2 ^ 2)·5,那么e = 2≥1且k = 5是奇数。) 可以证明至少对于a

因此,对于n = 21,我们可以证明至少a <21的一半,我们将拥有a ^ 5≢1和a ^ 5≢20以及a ^ 10≢20。我们得到下表。 (所有值均以21为模):

+----+-----+-------+
| a  | a^5 |  a^10 | MILLER–RABIN WITNESS? 
+----+-----+-------+
|  1 |   1 |     1 | NO, a^5 ≡ 1
|  2 |  11 |    16 | YES
|  3 |  12 |    18 | YES
|  4 |  16 |     4 | YES
|  5 |  17 |    16 | YES
|  6 |   6 |    15 | YES
|  7 |   7 |     7 | YES
|  8 |   1 |     1 | NO, a^5 ≡ 1
|  9 |  18 |     9 | YES
| 10 |  19 |     4 | YES
| 11 |   2 |     4 | YES
| 12 |   3 |     9 | YES
| 13 |  13 |     1 | YES
| 14 |  14 |     7 | YES
| 15 |  15 |    15 | YES
| 16 |   4 |    16 | YES
| 17 |   5 |     4 | YES
| 18 |   9 |    18 | YES
| 19 |  10 |    16 | YES
| 20 |  20 |     1 | NO, a^5 ≡ 20
+----+-----+-------+

当然,a <21的一半以上(实际上,超过75%)满足所有三个全等式a ^ 5≢1,a ^ 5≢20和a ^ 10≢20。 (我们称其为Miller–Rabin见证人;因为他们见证了n不是质数的事实。通常,许多素数检验都依赖于所有素数均具有的某些属性-如果您证明此类属性对某些数均无效,则这个数字不能是素数。见证人越多,素数测试的效果就越好。)

编辑。作为说明素数的一个示例,这是n = 13的表。自然,不可能有任何Miller–Rabin的13位见证人,因为它是素数。没有非原始的见证。由于n = 13,我们有n-1 = 12 =(2 ^ 2)·3,因此e = 2≥1且k = 3是奇数。因此,正如基思·康拉德(Keith Conrad)说明文件的第1页所述,所有a <13将满足三个全等式a ^ 3≡1,a ^ 3≡12,a ^ 6≡12中的至少一个。并且足够确定:

+----+-----+-------+
| a  | a^3 |   a^6 | MILLER–RABIN WITNESS? 
+----+-----+-------+
|  1 |   1 |     1 | NO, a^3 ≡ 1
|  2 |   8 |    12 | NO, a^6 ≡ 12
|  3 |   1 |     1 | NO, a^3 ≡ 1
|  4 |  12 |     1 | NO, a^3 ≡ 12
|  5 |   8 |    12 | NO, a^6 ≡ 12
|  6 |   8 |    12 | NO, a^6 ≡ 12
|  7 |   5 |    12 | NO, a^6 ≡ 12
|  8 |   5 |    12 | NO, a^6 ≡ 12
|  9 |   1 |     1 | NO, a^3 ≡ 1
| 10 |  12 |     1 | NO, a^3 ≡ 12
| 11 |   5 |    12 | NO, a^6 ≡ 12
| 12 |  12 |     1 | NO, a^3 ≡ 12
+----+-----+-------+

答案 1 :(得分:1)

根据@Memes的回答,我已经继续并为其添加了方案代码:

(define (display-all . vs)
  (for-each display vs))

(define (find-e-k n)
  (define (find-e-k-iter possible-k possible-e)
    (if (= (remainder possible-k 2) 0)
        (find-e-k-iter (/ possible-k 2) (+ possible-e 1))
        (values possible-e possible-k)))
  (find-e-k-iter (- n 1) 0))

; first-witness-case-test: (a ^ k) mod n # 1
(define (first-witness-case-test a k n)
  (not (= (expmod a k n) 1)))

; second-witness-case-test: all a ^ ((2 ^ i) * k) (with i = {0..e-1}) mod n # (n - 1)
(define (second-witness-case-test a e k n)
  (define (second-witness-case-test-iter a i k n)
    (cond ((= i -1) true)
          (else (let ()
                 (define witness (not (= (expmod a (* (fast-expt 2 i) k) n) (- n 1))))
                 (if witness
                 (second-witness-case-test-iter a (- i 1) k n)
                 false)))))
  (second-witness-case-test-iter a (- e 1) k n))

(define (miller-rabin-test n)
  (define (try-it a e k)
    (if (and (first-witness-case-test a k n) (second-witness-case-test a e k n))
        (display-all "is not prime, with a = " a "\n")
        (if (< a (- n 1))
            (try-it (+ a 1) e k)
            (display "is prime\n"))))
  (cond ((< n 2) (display "not prime"))
        ((= (remainder n 2) 0) (display "not prime\n"))
        (else (let ()
               (define-values (e k) (find-e-k n))
               (try-it 1 e k)))))

答案 2 :(得分:0)

  不能被愚弄的费马测验的一个变种叫做米勒-拉宾测验(Miller 1976; Rabin 1980)。这从费马小定理的另一种形式开始,该定理指出,如果n是素数并且a是小于n的任何正整数,则升到(n-1)次幂等于1模n。 / p>
  • n是素数,a a ^(n-1)= 1(mod n)
  

要通过Miller-Rabin检验测试数字n的素数,我们选择一个随机数a

因此随机选择一个,并检查a ^(n-1)= 1(mod n)。如果不是不是,那么您知道n不是素数。

  

但是,每当我们在expmod中执行平方步骤时,我们都会检查是否发现了``1模n的非平凡平方根'',即一个不等于1或n-1的平方等于1模n。

这是关于在expmod函数中添加额外的检查。这可能是您忽略的事情。

  

有可能证明如果存在这样一个非平凡的平方根1,那么n不是素数。

让我们详细介绍一下。非平凡平方根1将是一个数字x,使得x ^ 2 = 1(mod n)。并且x不为1或-1。

为什么其中之一表明n不是素数?

我们知道x ^ 2-1 =(x-1)(x + 1)和(工作模n)x-1和x + 1都不为零,但其乘积为零。这意味着我们有一个复合模量,您可以通过取这两个值的GCD来将其分解。

  

也有可能证明,如果n是不是质数的奇数,那么对于至少a

这又是在谈论将内部测试添加到expmod函数的平方分支。

  

修改expmod过程以发出是否发现平凡根为1的信号,并使用该过程以类似于fermat-test的过程实施Miller-Rabin测试。通过测试各种已知的质数和非质数来检查您的过程。提示:生成expmod信号的一种简便方法是使其返回0。

希望有帮助!询问是否需要其他指导。

答案 3 :(得分:0)

我找到了一篇涵盖该测试的论文,并证明了特定结果的证明,即2到n-2之间的值的一半以上将产生非平凡的平方根1。(定理4.1)

我使这段代码仔细检查了

(define (print x) (display x) (newline))

(define (assert p) (unless p (error 'assert-failed)))

(define (power-of-two-split m)
  ;; write m = 2^e k
  (let loop ((e 0) (k m))
    (if (even? k)
        (loop (+ e 1) (quotient k 2))
        (cons e k))))

(define (exp/mod a k n)
  ;; a^k (mod n)
  (cond ((= k 0) 1)
        ((= k 1) (modulo a n))
        (else (modulo (* a (exp/mod a (- k 1) n)) n))))

(define (miller-rabin a n)
  (assert (odd? n))
  (assert (= 3 (modulo n 4))) ;; only handles e=1 case, need to use power-of-two-split for full test
  (let ((k (quotient (- n 1) 2)))
    (exp/mod a k n)))

(define (test n)
  (for ((i (in-range 2 (- n 2))))
    (let ((m (miller-rabin i n)))
      (print `(,i -> ,m squared ,(exp/mod m 2 n))))))

(test 15)

它显示以下结果

(2 -> 8 squared 4)
(3 -> 12 squared 9)
(4 -> 4 squared 1)
(5 -> 5 squared 10)
(6 -> 6 squared 6)
(7 -> 13 squared 4)
(8 -> 2 squared 4)
(9 -> 9 squared 6)
(10 -> 10 squared 10)
(11 -> 11 squared 1)
(12 -> 3 squared 9)

因此,按照米勒阿拉伯证人的正式定义核实,他们实际上是全部小姑娘:

  

定义2.3。对于奇数n> 1,用k个奇数写n-1 = 2 ^ ek并选择a∈{1,。 。 。 ,n − 1}。如果所有的全等都是假的,我们说a是n的Miller–Rabin证人:   *对于所有i∈{0,...,a ^ k = 1 mod n和a ^ {(2 ^ i)k} = -1 mod n。 。 。 ,e − 1}。

您会看到'm'列中的所有值都不为1,而平方列中的任何值都不为14。因此它们都是见证人,因此> 50%是。

正在执行“ check-nontrivial-sqrt1”的代码在n = 3(mod 4)的特定情况下不相关,因为在这种情况下e = 1。


更新:

我刚刚意识到我们有很多证人的原因,但是我们并不总是从他们身上找到平方根:

  

米勒-拉宾证人的想法是找到一个意外的1 mod n的平方根。这并不总是我们真正发现的,因为质数n的前提n-1≡1 mod n对于复合n可能并不正确。

这是一张n = 15的a ^(n-1)(mod n)表

(2 -> 4)
(3 -> 9)
(4 -> 1)
(5 -> 10)
(6 -> 6)
(7 -> 4)
(8 -> 4)
(9 -> 6)
(10 -> 10)
(11 -> 1)
(12 -> 9)

如您所见,一致性a ^(n-1)= 1(mod n)实际上只有两次。