I want to be able to generate repeatable numbers using rand
in Clojure. (Specifically, I want results of calls to rand-nth
or Incanter's sample
to be repeatable, and these call rand-int
which in turn calls rand
).
I figured out from this question that if I use clojure.data.generators, I can reset the random state:
(require '[clojure.data.generators :as gen])
(alter-var-root #'gen/*rnd* (constantly (java.util.Random. 437)))
(gen/reservoir-sample 5 (range 1000)) ; => [940 591 636 12 755]
(gen/reservoir-sample 5 (range 1000)) ; => [376 540 827 307 463]
; reset random state:
(alter-var-root #'gen/*rnd* (constantly (java.util.Random. 437)))
; now the same results are generated again:
(gen/reservoir-sample 5 (range 1000)) ; => [940 591 636 12 755]
(gen/reservoir-sample 5 (range 1000)) ; => [376 540 827 307 463]
However, that method only seems to affect functions in clojure.data.generators, which isn't surprising:
(alter-var-root #'gen/*rnd* (constantly (java.util.Random. 437)))
(rand) ; => 0.9372552374760151
(rand) ; => 0.2712729314667742
; reset random state:
(alter-var-root #'gen/*rnd* (constantly (java.util.Random. 437)))
; not same results as before:
(rand) ; => 0.630238593767316
(rand) ; => 0.426744420572015
How can I restore the random state in such as way as to get repeatable results from rand
? So far I haven't found any documentation about this.
(Another question sounds as if it might be the same issue, but it's asking about something completely different.)
A clean way:
(ns designed.ly.rand)
(def ^:dynamic *rand* clojure.core/rand)
(defn rand-1
([]
(*rand* 1))
([n]
(*rand* n)))
(defmacro with-rand-seed
"Sets seed for calls to random in body. Beware of lazy seqs!"
[seed & body]
`(let [g# (java.util.Random. ~seed)]
(binding [*rand* #(* % (.nextFloat g#))]
(with-redefs [rand rand-1]
~@body))))
It redefines rand within the scope. Example:
(with-rand-seed 9
(rand 4) ; => 2.9206461906433105
(rand-int 10)) ; => 2
BTW. Beware of lazy seqs: http://kotka.de/blog/2009/11/Taming_the_Bound_Seq.html (Apparently, this link redirects to https without a trusted certificate so here's a link to a version at Web Archive: https://web.archive.org/web/20120505012701/http://kotka.de/blog/2009/11/Taming_the_Bound_Seq.html).
just wanted to add, for anyone still looking at this, there is already a library on clojars since 2015 that does this:
https://github.com/trystan/random-seed
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With