我正在编写Monty Hall模拟器,并且发现需要生成一个范围内的数字,不包括一个数字。
这看起来很容易,所以我天真地写道:
(g/...
函数是我个人库的一部分。它们的使用应该相当清楚):
(defn random-int-excluding
"Generates a random number between min-n and max-n; excluding excluding-n.
min-n is inclusive, while max-n is exclusive."
[min-n max-n excluding-n rand-gen]
(let [rand-n (g/random-int min-n max-n rand-gen)
rand-n' (if (= rand-n excluding-n) (inc rand-n) rand-n)]
(g/wrap rand-n' min-n (inc max-n))))
这会在范围内生成一个随机数,如果它等于排除的数字,则添加一个;必要时包装。当然,最终在被排除的数字之后给出数字的两倍被挑选的机会,因为如果选择它或被排除的数字,它将被挑选。样本输出频率范围为0到10(最大不包括),不包括2:
([0 0.099882]
[1 0.100355]
[3 0.200025]
[4 0.099912]
[5 0.099672]
[6 0.099976]
[7 0.099539]
[8 0.100222]
[9 0.100417])
然后我读了this answer,这似乎更简单了,基于它,写了:
(defn random-int-excluding
"Generates a random number between min-n and max-n; excluding excluding-n.
min-n is inclusive, while max-n is exclusive."
[min-n max-n excluding-n rand-gen]
(let [r1 (g/random-int min-n excluding-n rand-gen)
r2 (g/random-int (inc excluding-n) max-n rand-gen)]
(if (g/random-boolean rand-gen) r1 r2)))
基本上,它将范围拆分为2个较小的范围:从最小值到排除数字,从排除数字+ 1到最大值。它从这些范围生成随机数,然后随机选择其中一个。不幸的是,正如我在答案中指出的那样,除非两个分区的大小相等,否则会产生偏差。样本输出频率;与上述条件相同:
([0 0.2499497]
[1 0.2500795]
[3 0.0715849]
[4 0.071297]
[5 0.0714366]
[6 0.0714362]
[7 0.0712715]
[8 0.0715285]
[9 0.0714161])
请注意,排除数字之前较小范围的数字部分更有可能。为了解决这个问题,我不得不倾斜它来更频繁地从更大的范围中选择数字,而且实际上,我在数学方面不够精通,无法理解如何做到这一点。
我查看了链接问题中接受的答案,但对我来说,这似乎是我第一次尝试的一个版本,它接受超过1个数字来排除。我希望,与回答者所声称的一样,排除范围结束时的数字会受到青睐,因为如果选择的数字在排除范围内,它只会提前超过范围。
由于这将成为模拟中最常被调用的功能之一,我真的希望避免使用"蛮力"排除生成的数字时循环的方法,因为范围只有3个数字,所以每次尝试需要再次尝试的概率为1/3。
是否有人知道从连续范围中选择随机数的简单算法,但排除一个数字?
答案 0 :(得分:6)
要生成除[a, b]
之外的c
范围内的数字,只需生成[a, b-1]
范围内的数字,如果结果为c
,则输出{{1相反。
答案 1 :(得分:2)
如果可接受答案集合的大小很小,只需将所有值放入向量并使用rand-nth
:
http://clojuredocs.org/clojure.core/rand-nth
(def primes [ 2 3 5 7 11 13 17 19] )
(println (rand-nth primes))
(println (rand-nth primes))
(println (rand-nth primes))
~/clj > lein run
19
13
11
如果某些值应包含的值多于其他值,则只需将它们放在值数组中多次。每个值的出现次数决定了它的相对权重:
(def samples [ 1 2 2 3 3 3 4 4 4 4 ] )
(def weighted-samples
(repeatedly #(rand-nth samples)))
(println (take 22 weighted-samples))
;=> (3 4 2 4 3 2 2 1 4 4 3 3 3 2 3 4 4 4 2 4 4 4)
如果我们想要1到5之间的任何数字,但从不需要3,请执行以下操作:
(def samples [ 1 2 4 5 ] )
(def weighted-samples
(repeatedly #(rand-nth samples)))
(println (take 22 weighted-samples))
(1 5 5 5 5 2 2 4 2 5 4 4 5 2 4 4 4 2 1 2 4 1)
答案 2 :(得分:2)
只需生成一个懒惰的序列并过滤掉您不想要的项目:
chmod 777 config
映射到不同新值的其他方法的优势:此功能也适用于different discrete random number distributions。
答案 3 :(得分:1)
只是为了展示我写的实现,这里有什么对我有用:
(defn random-int-excluding
"Generates a random number between min-n and max-n; excluding excluding-n.
min-n is inclusive, while max-n is exclusive."
[min-n max-n excluding-n rand-gen]
(let [rand-n (g/random-int min-n (dec max-n) rand-gen)]
(if (= rand-n excluding-n)
(dec max-n)
rand-n)))
这给出了一个很好的均匀分布:
([0 0.111502]
[1 0.110738]
[3 0.111266]
[4 0.110976]
[5 0.111162]
[6 0.111266]
[7 0.111093]
[8 0.110815]
[9 0.111182])
答案 4 :(得分:1)
只需明确Alan Malloy's answer:
(defn rand-int-range-excluding [from to without]
(let [n (+ from (rand-int (dec (- to from))))]
(if (= n without)
(dec to)
n)))
(->> #(rand-int-range-excluding 5 10 8)
repeatedly
(take 100)
frequencies)
;{6 28, 9 22, 5 29, 7 21}
无需投票:)。