SICP 問題 1.27 (Carmichael数に騙されるの巻)
【問題】
脚注47のCarmichael数が本当にFermatテストを騙すことを示せ。
それには整数 n をとり、a < n なる全ての a で、a^n が n を法として a の合同になるかどうか見る手続きを書き、Carmichael 数でその手続きを使ってみる。
【解答】
まずはその脚注47をご紹介。
提示されている Charmichael数は、561, 1105, 1729, 2465, 2821, 6601 の6個。
47
Fermat テストを騙す数は、Carmichael数(Carmichael numbers)といい、非常に稀だということ以外はよく分からない。100,000,000 以下の Carmichael数は 255 個あり、小さい方のいくつかは 561, 1105, 1729, 2465, 2821, 6601である。
ランダムに選んだ非常に大きい数の素数性のテストで、Fermatテストを騙す数に遭遇する確率は、宇宙線が計算機の「正しい」アルゴリズムに誤りを生じさせる確率より小さい。
アルゴリズムが前の理由から不適切であり、後の理由からとしないのは数学と工学の違いを見せる。
こいつらに、今から作る総当たりFermatテスト関数を摘要してやる。
;Carmichaelテスト用関数。 ;引数で渡されたnが素数かどうかを真理値で返す。 (define (realy-prime? n) (fast-prime? n (- n 1))) ;Fermatテスト関数を指定回数実行する関数 ;nが評価対象の数で、iで回数を指定。 (define (fast-prime? n i) (cond ((= i 0) #t) ((carmichael-test i n) (fast-prime? n (- i 1))) (else #f))) ;新Fermatテスト関数。 ;基数aをランダム抽出するのではなく外から引数で指定してやる (define (carmichael-test a n) (= (expmod a n n) a)) ;お馴染み、冪剰余を算出する関数 (define (expmod base exp m) (cond ((= exp 0) 1) ((even? exp) (remainder (square (expmod base (/ exp 2) m)) m)) (else (remainder (* base (expmod base (- exp 1) m)) m)))) ;おまけ ;実行するときに定義されていないと動かないので一応。 (define (square x) (* x x))
上記を全て読み込ませてチェック!
見事に全て素数判定!
gosh> (map realy-prime? '(561 1105 1729 2465 2821 6601))
(#t #t #t #t #t #t)
gosh>
んが、以前作ったsmallest-divisor関数を読み込んで、
;最小除数を求める関数 (define (smallest-divisor n) (find-divisor n 2)) ;除数を求める処理 (define (find-divisor n test-divisor) (cond ((> (square test-divisor) n) n) ((divides? test-divisor n) test-divisor) (else (find-divisor n (+ test-divisor 1))))) ;除数かどうかの判別処理 (define (divides? a b) (= (remainder b a) 0))
これで判定してやると・・・
素数ではない!だまされている!
gosh> (map smallest-divisor '(561 1105 1729 2465 2821 6601))
(3 5 7 5 7 7)
gosh>