我希望能够在Clojure中使用rand
生成可重复的数字。 (具体来说,我希望调用rand-nth
或Incanter sample
的结果可重复,并调用rand-int
,然后调用rand
。)
我从this question想出,如果我使用clojure.data.generators,我可以重置随机状态:
(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]
但是,该方法似乎只影响clojure.data.generators中的函数,这并不奇怪:
(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
如何恢复随机状态,以便从rand
获得可重复的结果?到目前为止,我还没有找到任何关于此的文档。
(Another question听起来似乎可能是同一个问题,但它会询问完全不同的东西。)
答案 0 :(得分:4)
可能不是最干净的方式,但你可以通过重新定义clojure.core/rand
来实现它:
(ns clojure.core)
(def r (java.util.Random. 1))
(defn rand
([] (.nextDouble r))
([n] (.nextInt r n)))
(take 10 (repeatedly #(rand-int 10)))
每次运行时都会产生(5 8 7 3 4 4 4 6 8 8)。
答案 1 :(得分:2)
以下是如何定义可以在评论中描述的种子的函数生成器。
如果您不想要双打,请参阅Javadoc。
user=> (defn randfn
#_=> ([] (randfn (java.util.Random.)))
#_=> ([r] #(.nextDouble r)))
#'user/randfn
user=> (def source1 (randfn))
#'user/source1
user=> (source1)
0.6270662940925175
user=> (source1)
0.23351789802762046
以下是使用种子随机数生成器创建它的方法。
user=> (def source2 (randfn (java.util.Random. 37)))
#'user/source2
user=> (take 3 (repeatedly #(source2)))
(0.7276532767062343 0.5136790759391296 0.7384220244718898)
user=> (def source3 (randfn (java.util.Random. 37)))
#'user/source3
user=> (take 3 (repeatedly #(source3)))
(0.7276532767062343 0.5136790759391296 0.7384220244718898
作为奖励,您还可以使用新的ThreadLocalRandom或非常新的SecureRandom作为随机数生成器。
user=> (def secure-source (randfn (java.security.SecureRandom.)))
#'user/secure-source
user=> (take 3 (repeatedly #(secure-source)))
(0.9987555822097023 0.48452119609266475 0.443029180668418)
答案 2 :(得分:2)
干净的方式:
(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))))
它在范围内重新定义了兰特。例如:
(with-rand-seed 9
(rand 4) ; => 2.9206461906433105
(rand-int 10)) ; => 2
顺便说一句。请注意懒惰的序列:http://kotka.de/blog/2009/11/Taming_the_Bound_Seq.html(显然,此链接会重定向到没有受信任证书的https,因此这里是指向Web Archive版本的链接:https://web.archive.org/web/20120505012701/http://kotka.de/blog/2009/11/Taming_the_Bound_Seq.html)。
答案 3 :(得分:2)
Clojure的rand
依赖于random
中的java.lang.Math
方法:
user=> (source rand)
(defn rand
"Returns a random floating point number between 0 (inclusive) and
n (default 1) (exclusive)."
{:added "1.0"
:static true}
([] (. Math (random)))
([n] (* n (rand))))
根据https://docs.oracle.com/javase/7/docs/api/java/lang/Math.html#random()
上的Oracle Java 7文档public static double random()
返回带有正号的double值,大于或等于0.0且小于1.0。返回值是伪随机选择的,具有来自该范围的(近似)均匀分布。
首次调用此方法时,它会创建一个新的伪随机数生成器,就像表达式
一样new java.util.Random()
此新的伪随机数生成器此后用于对此方法的所有调用,并且在其他任何地方都没有使用。
此方法已正确同步,以允许多个线程正确使用。但是,如果许多线程需要以很高的速率生成伪随机数,则可以减少每个线程争用自己的伪随机数生成器的争用。
返回:
大于或等于0.0且小于1.0的伪随机双。
如果您浏览java.lang.Math
文档,您将看到它没有允许设置随机数生成器种子的API。使用random()
API的代码无法设置种子或保留随机数生成器的不同副本。
最初的问题是:
我希望能够在Clojure中使用rand生成可重复的数字。 [例如。 clojure.core /兰特]
除非您使用自己的rand功能,否则这是不可能的。因此,我建议使用clojure.data.generators而不是clojure.core/rand
,将随机数生成器公开为动态变量*rnd*
:
(def ^:dynamic ^java.util.Random
*rnd*
"Random instance for use in generators. By consistently using this
instance you can get a repeatable basis for tests."
(java.util.Random. 42))
使用gen/*rnd*
,如下例所示:
(require '[clojure.data.generators :as gen])
(binding [gen/*rnd* (java.util.Random. 12345)]
(gen/int))
这总是返回-593551136
。
答案 4 :(得分:1)
在最初的问题中,有两件事被误解了:
首先,应通过binding
宏管理动态变量。第二个rand
不是clojure.data.generators
的函数,而是clojure.core
本身的函数,因此重置*rnd*
var并不起作用。所以这是你应该怎么做的:
(require '[clojure.data.generators :as gen])
(binding [gen/*rnd* (java.util.Random. 437)]
(println (gen/double)) ;=> 0.7634858067742888
(println (gen/double)) ;=> 0.6959205688388975
)
(binding [gen/*rnd* (java.util.Random. 437)]
(println (gen/double)) ;=> 0.7634858067742888
(println (gen/double)) ;=> 0.6959205688388975
)
答案 5 :(得分:1)
只是想补充一下,对于仍在查看此内容的任何人,自2015年以来,clojars上已经有一个库可以做到这一点:
答案 6 :(得分:0)
据我所知,以上都不是功能性方法。如果你想要,你可以使用lazy-seq:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<h2>Slide in Overlay from the Bottom</h2>
<p>Hover over the image to see the effect.</p>
<div class="container">
<img src="img_avatar.png" alt="Avatar" class="image">
<div class="overlay">
<div class="text">Hello World</div>
</div>
</div>
</body>
</html>
获取您使用的第一个随机数:
(letfn [(f [randomizer] (lazy-seq (cons (.nextInt randomizer) (f randomizer))))]
(defn create-random
([] (f (java.util.Random.)))
([seed] (f (java.util.Random. seed)))))
要获得您使用的其余序列:
(first (create-random 0)) ;; using 0 as seed
要获取第一个元素和对其余元素的引用,您可以使用:
(rest (create-random 0)) ;; again 0 as seed