同じランダムステートを使い回したい


ランダムステートの生成と利用

randomで乱数を得ることができますが、デフォルトの*random-state*とは別個にランダムステートを指定して利用することが可能です。
ランダムステートの生成には、make-random-stateを利用し、全く新規に作成、コピー等の使い分けが可能です。
同じランダムステートからは同じ結果が生じます。

;;; 新規にrandom-stateを生成
(defvar *my-random-state* (make-random-state t))
(defun foo ()
  (with-output-to-string (out)
    (dotimes (i 20) 
      (write (random 36 (make-random-state *my-random-state*))
             :stream out
             :base 36) )))


;;; ループ内で都度オリジナルをコピーするので同じ結果が繰り返される
(foo)
;=>  "YYYYYYYYYYYYYYYYYYYY"


(foo)
;=>  "YYYYYYYYYYYYYYYYYYYY"
(defun bar ()
  (with-output-to-string (out) 
    (let ((rs (make-random-state *my-random-state*)))
      (dotimes (i 20) 
        (write (random 36 rs)           ;randomは実行の度にrsを副作用的に変更する
               :stream out
               :base 36) ))))


;; ループの外でオリジナルをコピー。繰り返しの度に結果は変わるが同じ種からは、
;; 同じ結果が生じる

(bar)
;=>  "YNEPQBIY7UZGNJFG3E2X"

(bar)
;=>  "YNEPQBIY7UZGNJFG3E2X"

ランダムステートのシリアライズ

random-state型のオブジェクトが具体的にどのようなオブジェクトかは規格上決められていませんが、random-state型のオブジェクトをwriteなどで書き出したものは、同じ処理系でreadすることで同じ状態のオブジェクトとして復元できることが保証されています。

(let ((state (make-random-state)))
  (values state (read-from-string (write-to-string (make-random-state)))))
;=> #.(CCL::INITIALIZE-MRG31K3P-STATE 314159 42 1776 271828 6021023 1066),
;   #.(CCL::INITIALIZE-MRG31K3P-STATE 314159 42 1776 271828 6021023 1066)

別の処理系で書き出したものをreadできるかは保証されていません。注意してください。