Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to generate repeatable random sequences with rand-int

Tags:

clojure

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.)

like image 742
Mars Avatar asked Jun 03 '14 04:06

Mars


2 Answers

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).

like image 62
Marcin Bilski Avatar answered Sep 28 '22 02:09

Marcin Bilski


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

like image 34
danieltan95 Avatar answered Sep 28 '22 04:09

danieltan95