Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't repeatedly generate reproducible random numbers when using a seed in Clojure?

Tags:

random

clojure

I have some functions that require a series of random numbers so I've taken some simple primitives such as #(inc (g/uniform 0 n)) and I can't seem to generate a reproducible series of random numbers, even though I'm rebinding *rnd*, unless I generate them as shown below. I can't imagine that's the best way, so can anyone point out how to do it better?

Note: I run each example below three times as shown to produce the given results.

(ns example.show
  (:require [clojure.data.generators :as g]))


(binding [g/*rnd* (java.util.Random. 42)]
  (take 10 (repeatedly #(inc (g/uniform 0 n))))

=> (9 4 5 4 4 5 1 8 2 9)

=> (2 1 1 6 3 10 10 4 1 9)

=> (10 4 7 8 9 6 10 1 8 3)


(binding [g/*rnd* (java.util.Random. 42)]
  (g/reps 10 #(inc (g/uniform 0 n)))

=> (3 9 4 6 3 8 6 6 5 4)

=> (7 8 4 7 7 5 7 4 8 7)

=> (2 8 7 8 8 8 9 2 6 5)

;; This seems to work
(binding [g/*rnd* (java.util.Random. 42)]
  (letfn [(roll [n] #(inc (g/uniform 0 n)))]
    [((roll 10)) ((roll 10)) ((roll 10)) ((roll 10)) ((roll 10)) ((roll 10)) ((roll 10)) ((roll 10)) ((roll 10)) ((roll 10))]))

=> [8 7 4 3 7 10 4 3 5 8]

=> [8 7 4 3 7 10 4 3 5 8]

=> [8 7 4 3 7 10 4 3 5 8]
like image 808
toofarsideways Avatar asked Mar 16 '14 19:03

toofarsideways


2 Answers

Because of laziness. You return from binding before the sequence is realized. Therefore, the lazy sequence never sees the bindings you set. One solution would be to force realization with a doall on the sequence inside the binding.

like image 82
A. Webb Avatar answered Nov 17 '22 04:11

A. Webb


If, as your comment on the other answer indicates, you want to preserve the laziness, then you would need to apply the binding in the function passed to repeatedly, capturing a seed that you created outside of the lazy seq. For instance:

(defn rand-seq [seed]
  (let [r (java.util.Random. seed)]
    (repeatedly #(binding [g/*rnd* r]
                   (inc (g/uniform 0 10))))))

(take 10 (rand-seq 42))
#=> (8 7 4 3 7 10 4 3 5 8)

(take 10 (rand-seq 42))
#=> (8 7 4 3 7 10 4 3 5 8)
like image 22
Alex Avatar answered Nov 17 '22 04:11

Alex