SICP : Ex1.28
これは、素数判定を確率的なテストを用いて行うという問題。fermat-testというテストが本編で解説されていて、問題では、miller-rabin-testというテストを使って書き直すことが求められた。
この問題もConcurrent Cleanを使って回答します。
id:lethevert:20060108:p1 で定義した乱数ライブラリと、次のような関数をライブラリとして使用します。(これらは、今後の問題で、継続して使っていきます)
($) infixl 0 //:: (a -> b) a -> b ($) f a :== f a //累乗の剰余 expmod :: !Int !Int !Int -> Int expmod _ 0 _ = 1 expmod base exp m | exp < 0 = abort "^ (Int) called with negative power argument" = expmod exp where expmod :: !Int -> Int expmod 0 = 1 expmod exp | isEven exp = (rem) $ (expmod $ exp/2)^2 $ m | otherwise = (rem) $ base * (expmod $ exp-1) $ m
回答
コメントアウトした方がfermat-testです。
fast_prime_p :: Int Int -> Bool fast_prime_p n times = fst $ fast_prime_p` times mkRandomInt where fast_prime_p` 0 r = (True, r) fast_prime_p` times r # (ok, r) = miller_rabin_test n r //fermat_test n r | ok = fast_prime_p` (times-1) r | otherwise = (False, r) fermat_test :: Int *(Random Int) -> (Bool, *(Random Int)) fermat_test n r # (i, r) = nextRand r = (try_it $ 1 + i rem (n-1), r) where try_it a = a == expmod a n n miller_rabin_test :: Int *(Random Int) -> (Bool, *(Random Int)) miller_rabin_test n r # (i, r) = nextRand r = (try_it $ 1 + i rem (n-1), r) where try_it a = 1 == expmod a (n-1) n