生成随机数,不包括单个数字

时间:2016-11-08 23:25:41

标签: random clojure

我正在编写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。

是否有人知道从连续范围中选择随机数的简单算法,但排除一个数字?

5 个答案:

答案 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}

无需投票:)。