計算機プログラムの構造と解釈 第二版 P29 問題1.25
とりあえずは、ここに書いてありますexpmodを実装してみる。
1 #!/usr/local/bin/gosh 2 ;; -*- coding: utf-8 -*- 3 4 (use ggc.debug.trace) 5 (use math.mt-random) 6 7 ;;random;;;;;;;;;;;;;;;;;;;;;; 8 (define mt (make <mersenne-twister> :seed (sys-time))) 9 (define (random n) 10 (mt-random-integer mt n)) 11 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 12 13 14 ;;runtime;;;;;;;;;;;;;;;;;;;;;; 15 (define (runtime) 16 (use srfi-11) 17 (let-values (((a b) (sys-gettimeofday))) 18 (+ 1000000 b))) 19 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 20 21 ;;1-21;;;;;;;;;;;;;;;;;;;;;;;;; 22 (define (square n) 23 (* n n)) 24 25 26 (define (smallest-divisor n) 27 (find-divisor n 2)) 28 29 (define (find-divisor n test-divisor) 30 (cond ((> (square test-divisor) n) n) 31 ((divides? test-divisor n) test-divisor) 32 (else (find-divisor n 33 (+ test-divisor 1))))) 34 35 36 (define (divides? a b) 37 (= (remainder b a) 0)) 38 39 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 40 41 42 ;;P28 - P29;;;;;;;;;;;;;;;;;;;;;;;;; 43 44 ;;ここは前までの実装なのでcomment out 45 ;(define (expmod base exp m) ;base = a, exp =n, m = n 46 ; (cond ((= exp 0) 1) 47 ; ((even? exp) 48 ; (remainder (square (expmod base (/ exp 2) m)) 49 ; m)) 50 ; (else 51 ; (remainder (* base (expmod base (- exp 1) m)) 52 ; m)))) 53 54 ;;問題1.25 expmod実装 55 (define (fast-expt b n) ;base = a, exp =n, m = n 56 (cond ((= n 0) 1) 57 ((even? n) (square (fast-expt b (/ n 2)))) 58 (else (* b (fast-expt b (- n 1)))))) 59 60 (define (expmod base exp m) 61 (remainder (fast-expt base exp) m)) 62 ;;;問題1.25 expmod実装ここまで。 63 64 65 66 (define (fermat-test n) 67 (define (try-it a) 68 (= (expmod a n n) a)) 69 (try-it (+ 1 (random (- n 1))))) 70 71 (define (fast-prime? n times) 72 (cond ((= times 0) #t) 73 ((fermat-test n) (fast-prime? n (- times 1))) 74 (else #f))) 75 76 (define (prime? n) 77 (fast-prime? n 5)) 78 79 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 80 81 82 (define (timed-prime-test n) 83 (newline) 84 (display n) 85 (start-prime-test n (runtime))) 86 87 (define (start-prime-test n start-time) 88 (if (prime? n) 89 (report-prime (- (runtime) start-time)))) 90 91 (define (report-prime elapsed-time) 92 (display " *** ") 93 (display elapsed-time)) 94 95 (define (search-for-primes-iter num counter) 96 (cond ((= counter 3)) 97 ((prime? num) (print num) (search-for-primes-iter (+ num 2) (+ counter 1))) 98 (else (search-for-primes-iter (+ num 2) counter)))) 99 100 101 (define (search-for-primes num) 102 (cond ((divides? 2 num) (search-for-primes-iter (+ num 1) 0)) 103 (else (search-for-primes-iter num 0)))) 104 105 106 ;; main 107 (define (main args) 108 (search-for-primes 1000) 109 (search-for-primes 10000) 110 (search-for-primes 100000) 111 (search-for-primes 1000000) 112 (search-for-primes 10000000) 113 (search-for-primes 100000000) 114 (search-for-primes 1000000000) 115 (search-for-primes 10000000000) 116 (search-for-primes 100000000000) 117 118 119 (timed-prime-test 1009) 120 (timed-prime-test 1013) 121 (timed-prime-test 1019) 122 123 (timed-prime-test 10007) 124 (timed-prime-test 10009) 125 (timed-prime-test 10037) 126 127 (timed-prime-test 100003) 128 (timed-prime-test 100019) 129 (timed-prime-test 100043) 130 131 (timed-prime-test 1000003) 132 (timed-prime-test 1000033) 133 (timed-prime-test 1000037) 134 135 (timed-prime-test 10000019) 136 (timed-prime-test 10000079) 137 (timed-prime-test 10000103) 138 139 (timed-prime-test 100000007) 140 (timed-prime-test 100000037) 141 (timed-prime-test 100000039) 142 143 (timed-prime-test 1000000007) 144 (timed-prime-test 1000000009) 145 (timed-prime-test 1000000021) 146 147 (timed-prime-test 10000000019) 148 (timed-prime-test 10000000033) 149 (timed-prime-test 10000000061) 150 151 (timed-prime-test 100000000003) 152 (timed-prime-test 100000000019) 153 (timed-prime-test 100000000057) 154 (newline) 155 0)
実行。。。 。 。 。 。 。 。
遅すぎる!!
なので、とりあえず、使えないことはわかる。
なぜ遅いか。考えてみた。
問題1.25のexpmod実装だと、べき乗を求めてからremainderを実行しているしている。
その前の実装だとremainderのタイミングが多い。
remainderを逐次実行すると、積算で扱う数が小さくてすむ。
たとえばここでいうなら、通常のexmodであれば、(m-1)^2が最高にでかい乗算になる。
桁数が大きいと平方は、重そうなイメージ
だからだと思う。たぶん。きっと。