素数を読み上げる。
すっかりゆっくりの声で定着しているhttp://cncc.hp.infoseek.co.jp/を入れてみた。exeに文字列を投げれば、その場で読み上げてくれるので非常に使いやすい。
(defvar *softalk* "SofTalk.exe" "softalkのパス") (defun call-softalk (&optional (str "ゆ?")) (interactive) (call-process (format nil "~A ~A" *softalk* (if (interactive-p) (get-clipboard-data) str))))
パスをスペシャル変数に入れておけば、あとは適当に呼び出せる。とりあえず、クリップボードか渡した文字列を読み上げるコマンドを作っておく*1。あとは(call-softalk "ふんちゃら")で評価するなり、M-x call-softalk をするなり。
で、試してみると結構聞き取れるし、数字なんかは無量大数まで読んでくれる。そこで、適当に素数を読み上げるうざいスクリプトを書いてみた。標準では素数判定する関数がないので、適当にミラー–ラビン素数判定法 - Wikipediaを使う。
(defun prime-p (x &optional (k 20)) "フェルマー法→ミラー-ラビン法の順で素数判定する" (labels ((modexp2 (n) (loop (if (evenp n) (setq n (ash n -1)) (return n)))) (expmod (b p m) (let ((r 1)) (loop (if (oddp p) (setq r (mod (* r b) m))) (if (zerop (setq p (ash p -1))) (return r) (setq b (mod (* b b) m)))))) (composite-p (n a d) (let ((y (expmod a d n))) (loop (if (or (zerop (- n y 1)) (zerop (- n d 1)) (= y 1)) (return (and (evenp d) (not (zerop (- n y 1))))) (setq y (mod (* y y) n) d (* d 2)))))) (fermat-prime-p (n &optional (b 2)) (= (expmod b n n) b)) (mr-prime-p (n keys) (let ((d (modexp2 (1- n)))) (dolist (a keys t) (if (composite-p n a d) (return nil)))))) (cond ((= x 1) nil) ((= x 2) t) ((or (evenp x) (not (fermat-prime-p x))) nil) (t (let (keys) (mr-prime-p x (dotimes (a k keys) (push (+ (random (- x 3)) 2) keys))))))))
wikiにあったスクリプトがrubyを読み慣れてないせいで良く分からなかったのは秘密だ。これを用いて素数を探せば良いんだけど、ちょっと動作が重いので、コンパイルして動作を確認する。
(defun random-num (x) "ランダム数を桁指定で発生" (let ((num 0) (column 1)) (while (< -1 (decf x)) (setq num (+ num (* (random 10) column)) column (* column 10))) num)) > (compile 'prime-p) prime-p > (prime-p 17) t > (let ((x (random-num 10))) (format t "~A:~A" x (if (prime-p x) "prime" "composite"))) 7483941364:composite nil
また、あまり大きい数はrandomでつくれないので、そのための関数も用意する。ランダム数からではあんまり素数は見つからないけど(例では偶数だし)。実際は奇数にして、2と5の倍数くらい飛ばして探索すれば、ちゃんと見つかる。
(defun seek-prime (d) "桁指定で素数探索" (long-operation (let ((t1 (get-internal-real-time))) (let* ((try -1) (ti (get-internal-real-time)) x) (loop (when (< 1000 (- (get-internal-real-time) ti)) (setq ti (get-internal-real-time)) (format t ".")(refresh-screen)(do-events)) (when (zerop (mod (incf try) 100)) (format t "?") (setq x (random-num d)) (if (evenp x) (incf x))) (if (prime-p x) (return (values x try)) (if (= (mod x 5) 3) (incf x 4) (incf x 2)))) (format t "~%elapse ~A ms~%try:~A~%get:~A" (- (get-internal-real-time) t1) try x))))) > (seek-prime 300) ?..........?...........?..........?...........?..........?........ elapse 69265 ms try:576 get:228078007758460841141522675428554598943623641685694224034734044684058377250905302970548014500006030768266577036814659433055273877076386369748770904932791550461140825954509834695966882347417588017627459499299441642020758252350779275352230709323553164163590137007591168868856420080338322477577689283721 nil
300桁とかはみ出るな〜。でも普通に計算してくれる。桁数が多いと時間がかかるけど、無量大数程度なら、PCのスペックにもよるけれど、1秒もあれば出るはず。で、実際に動かすのはこっち。
(defun prime-softalk () (interactive) (let* ((try (1+ (random 72))) (x (random-num try))) (incf try (random 10)) (let ((prime-p (loop (if (prime-p x) (return t) (if (< 0 (decf try)) (incf x) (return nil)))))) (call-softalk (format nil "~Aは、素数~A~A" x (if prime-p "だよ。" "じゃないよ。") (case (mod x 5) (0 "バカなの? しぬの?") (1 "けらけらけら") (2 "ゆっくりしていってね?") (3 "どうでもいいけど") (t "")))) (+ (log x 12) 6)))) (defun toggle-prime-softalk () (interactive) (unless (stop-timer 'prime-softalk-loop) (prime-softalk-loop))) (defun prime-softalk-loop () (start-timer (prime-softalk) 'prime-softalk-loop t)) > (toggle-prime-softalk) t