如何在 Clojure 中包含 rand 的上限?

How do include the upper limit of `rand` in Clojure?

在Clojure的rand函数中,包含了下限0,但没有包含上限。 如何定义包含上限的rand等价函数?

编辑(简单解释):因为rand通过生成一个整数除以我的另一个整数来生成0到1之间的随机double,可以实现一个版本包含上限为

(defn rand-with-nbits [nbits]
  (let [m (bit-shift-left 1 nbits)]
    (/ (double (rand-int m))
       (double (dec m)))))

(apply max (repeatedly 1000 #(rand-with-nbits 3)))
;; => 1.0

实现中使用了53位,即(rand-with-nbits 53)

更长的答案:

看看 source code of rand. It internally calls Math.random, which in turn calls Random.nextDouble,它有以下实现:

private static final double DOUBLE_UNIT = 0x1.0p-53; // 1.0 / (1L << 53)
...
public double nextDouble() {
    return (((long)(next(26)) << 27) + next(27)) * DOUBLE_UNIT;
}

上面的代码本质上是生成一个介于02^53-1之间的随机整数,并将它除以2^53。要获得包含上限,您必须将其除以 2^53-1。上面的代码调用了受保护的 next 方法,所以为了让我们自己调整实现,我们使用 proxy:

(defn make-inclusive-rand [lbits rbits]
  (let [denom (double
               (dec           ; <-- `dec` makes upper bound inclusive
                (bit-shift-left
                 1 (+ lbits rbits))))
        g (proxy [java.util.Random] []
            (nextDouble []
              (/ (+ (bit-shift-left
                     (proxy-super next lbits) rbits)
                    (proxy-super next rbits))
                 denom)))]
    (fn [] (.nextDouble g))))

(def my-rand (make-inclusive-rand 26 27))

您不太可能生成上限:

(apply max (repeatedly 10000000 my-rand))
;; => 0.9999999980774417

但是,如果底层整数是用 更少的位 生成的,您会发现它有效:

(def rand4 (make-inclusive-rand 2 2))

(apply max (repeatedly 100 rand4))
;; => 1.0

对了,上面的实现是不是thread-safe