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

In Clojure's rand function, the lower limit 0 is included, but the upper limit is not. How to define a rand function equivalent which includes the upper limit?



Solution 1:[1]

Edit (simple explanation): Because rand generates a random double between 0 and 1 by generating an integer and dividing my another integer, you can implement a version with inclusive upper bound as

(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

In the implementation, 53 bits are used, that is (rand-with-nbits 53).

Longer answer:

Take a look at the source code of rand. It internally calls Math.random, which in turn calls Random.nextDouble, that has the following implementation:

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;
}

The above code essentially produces a random integer between 0 and 2^53-1 and divides it by by 2^53. To get an inclusive upper bound, you would instead have to divide it by 2^53-1. The above code calls the next method which is protected, so to make our own tweaked implementation, we use 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))

It is unlikely that you will ever generate the upper bound:

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

However, if the underlying integers are generated with fewer bits, you will see that it works:

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

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

By the way, the above implementation is not thread-safe.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1