8. Random Functions
The random/ library makes reproducible random values from a seed. It reads no
host entropy: random/seeded always takes an explicit seed, and the same seed
gives the same values on every host and in every later version.
Table of Contents
8.1. Generators ↑
A generator is a mutable value that the runtime owns. Each call that takes a
generator advances it in place, as regex/find advances a matcher. Two calls
on the same generator give the next two values of one sequence:
(let [g (random/seeded 42)]
[(random/int .g 0 6) (random/int .g 0 6)])
; [0 2] on every run
The algorithm is xoshiro256**, seeded through SplitMix64: the generator's four 64-bit state words are the first four SplitMix64 outputs for the seed. This choice is fixed.
A generator in a result is written as its algorithm and its current state, 64 lowercase hexadecimal digits for the four state words in order, each most significant digit first:
#rng {xoshiro256ss "9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb"}
The state written is the one the generator has when the result is exported,
after the whole evaluation, not when the generator is placed in the result. So
[.g (random/int .g 0 6)] shows in its first slot the state after the
random/int call.
A #rng value read back from a document, or bound from the host, becomes a
new generator that continues the same sequence. It changes into a generator in
place when a random function first uses it, so every reference to it shares
one state. A payload that is not one xoshiro256ss member with 64 lowercase
hexadecimal digits, or whose state is all zero, produces #err <invalid tag>
when a random function uses it. Two generators are equal when their states are
equal.
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) .g])
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) .g])
0 2 #rng {xoshiro256ss 9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb}
(let [g #rng {xoshiro256ss "9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb"}] [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) .g])
(let [g #rng {xoshiro256ss 9d1c68b67d1ceb4323acb714c3a78d019d431c524cad8dc572e82a070b0b9ddb}] [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) .g])
4 5 5 #rng {xoshiro256ss 7e3fedbea92a13a5c9a25ba0f11c828cc38346747039f414cf55c271f2386fa5}
(random/int #rng {xoshiro256ss "00"} 0 6) (random/int #rng {xoshiro256ss "0000000000000000000000000000000000000000000000000000000000000000"} 0 6) (random/int #rng {xoshiro256ss "6DB07C7DD404690B81DDC5FE6C15769825CFE223490D9D1F9252543D113B0C36"} 0 6) (random/int #rng {pcg32 "6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36"} 0 6) (random/int #rng 42 0 6) (random/int #rng {xoshiro256ss "bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394" extra true} 0 6)
(random/int #rng {xoshiro256ss 00} 0 6) (random/int #rng {xoshiro256ss 0000000000000000000000000000000000000000000000000000000000000000} 0 6) (random/int #rng {xoshiro256ss 6DB07C7DD404690B81DDC5FE6C15769825CFE223490D9D1F9252543D113B0C36} 0 6) (random/int #rng {pcg32 6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36} 0 6) (random/int #rng 42 0 6) (random/int #rng {<extra> xoshiro256ss bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394} 0 6)
#err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"} #err {<invalid tag> message "#rng requires a xoshiro256ss state of 64 hex digits, not all zero"}
Ranges are half-open: lo is included and hi is not. An empty range, where
lo > hi=, produces #err <invalid value>. A bound of the wrong type, or a
value that is not a generator, produces #err <mismatch type>.
8.2. random/seeded ↑
Function(random/seeded seed) → generator
Returns a new generator for the integer seed. Every integer is a seed,
negative ones included.
| Call | Result |
|---|---|
(random/seeded 42) |
#rng {xoshiro256ss bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394} |
(random/seeded 0) |
#rng {xoshiro256ss e220a8397b1dcdaf6e789e6aa1b965f406c45d188009454ff88bb8a8724c81ec} |
(random/seeded 1.5) |
#err <mismatch type> |
(random/seeded 42) (random/seeded 0) (random/seeded -1) (random/seeded -9223372036854775808)
#rng {xoshiro256ss bdd732262feb6e9528efe333b266f10347526757130f9f52581ce1ff0e4ae394} #rng {xoshiro256ss e220a8397b1dcdaf6e789e6aa1b965f406c45d188009454ff88bb8a8724c81ec} #rng {xoshiro256ss e4d971771b652c20e99ff867dbf682c9382ff84cb27281e96d1db36ccba982d2} #rng {xoshiro256ss 481ec0a212a9f3dbc46fa638a630901261a685ffc80a8140592e268383e356f9}
(let [a (random/seeded 7) b (random/seeded 7)] [(random/int .a 0 100) (random/int .b 0 100) (random/int .a 0 100) (random/int .b 0 100)])
(let [a (random/seeded 7) b (random/seeded 7)] [(random/int .a 0 100) (random/int .b 0 100) (random/int .a 0 100) (random/int .b 0 100)])
70 70 27 27
(random/seeded 1.5) (random/seeded abc) (random/seeded) (random/int 42 0 6) (random/int #other {xoshiro256ss "6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36"} 0 6)
(random/seeded 1.5) (random/seeded abc) (random/seeded) (random/int 42 0 6) (random/int #other {xoshiro256ss 6db07c7dd404690b81ddc5fe6c15769825cfe223490d9d1f9252543d113b0c36} 0 6)
#err {<mismatch type> message "random/seeded requires an integer seed"} #err {<mismatch type> message "random/seeded requires an integer seed"} #err {<arity mismatch> message "wrong number of arguments"} #err {<mismatch type> message "expected a random generator"} #err {<mismatch type> message "expected a random generator"}
8.3. random/int ↑
Function(random/int generator lo hi) → integer
Returns an integer x with lo < x < hi=, uniform over the range. The range
can span all 64-bit integers. Integers use Lemire's multiply-and-reject method,
so no value is more likely than another.
Call, on (random/seeded 42) |
Result |
|---|---|
(random/int .g 0 6) |
0 |
(random/int .g -10 -5) |
-10 |
(random/int .g -9223372036854775808 9223372036854775807) |
-7676373272452217067 |
(random/int .g 5 5) |
#err <invalid value> |
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6)]) (let [g (random/seeded 42)] [(random/int .g -10 -5) (random/int .g 5 6) (random/int .g -1000000000000000000 1000000000000000000)]) (let [g (random/seeded 7)] [(random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3)])
(let [g (random/seeded 42)] [(random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6) (random/int .g 0 6)]) (let [g (random/seeded 42)] [(random/int .g -10 -5) (random/int .g 5 6) (random/int .g -1000000000000000000 1000000000000000000)]) (let [g (random/seeded 7)] [(random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3) (random/int .g 0 3)])
[[0 2 4 5 5 4 4 5 4 3] [-10 5 360086822056278777] [2 0 2 2 2]]
(let [g (random/seeded 42)] [(random/int .g -9223372036854775808 9223372036854775807) (random/int .g -9223372036854775808 9223372036854775807) (random/int .g -9223372036854775808 9223372036854775807)]) (let [g (random/seeded 42)] [(random/int .g -1000000000000000000 1000000000000000000) (random/int .g -1000000000000000000 1000000000000000000) (random/int .g -1000000000000000000 1000000000000000000)])
(let [g (random/seeded 42)] [(random/int .g -9223372036854775808 9223372036854775807) (random/int .g -9223372036854775808 9223372036854775807) (random/int .g -9223372036854775808 9223372036854775807)]) (let [g (random/seeded 42)] [(random/int .g -1000000000000000000 1000000000000000000) (random/int .g -1000000000000000000 1000000000000000000) (random/int .g -1000000000000000000 1000000000000000000)])
[[-7676373272452217067 -2232420343890232707 3321214725393783200] [-832274057880235478 -242039498674662721 360086822056278777]]
(let [g #rng {xoshiro256ss "0000000000000001000000000000000000000000000000010000000000000001"}] [(random/int .g 0 6) .g]) (let [g #rng {xoshiro256ss "0000000000000001000000000000000000000000000000010000000000000001"} warm (random/seeded 7)] (for [i (lists/range 48)] (random/int .warm 0 2)) [(random/int .g 0 6) .g])
(let [g #rng {xoshiro256ss 0000000000000001000000000000000000000000000000010000000000000001}] [(random/int .g 0 6) .g]) (let [g #rng {xoshiro256ss 0000000000000001000000000000000000000000000000010000000000000001} warm (random/seeded 7)] (for [i (lists/range 48)] (random/int .warm 0 2)) [(random/int .g 0 6) .g])
[[0 #rng {xoshiro256ss 0000000004000080000020000400000040000000040000000010000004000000}] [0 #rng {xoshiro256ss 0000000004000080000020000400000040000000040000000010000004000000}]]
(random/int (random/seeded 1) 5 5) (random/int (random/seeded 1) 6 0) (random/int (random/seeded 1) 9223372036854775807 -9223372036854775808)
#err {<invalid value> message "random range is empty: lo must be below hi"} #err {<invalid value> message "random range is empty: lo must be below hi"} #err {<invalid value> message "random range is empty: lo must be below hi"}
(random/int (random/seeded 1) 0.0 6.0) (random/int (random/seeded 1) 0 6.5) (random/int (random/seeded 1) a z) (random/int (random/seeded 1) 6)
#err {<mismatch type> message "random/int requires integer bounds"} #err {<mismatch type> message "random/int requires integer bounds"} #err {<mismatch type> message "random/int requires integer bounds"} #err {<arity mismatch> message "wrong number of arguments"}
8.4. random/float ↑
Function(random/float generator lo hi) → float
Returns a float x with lo < x < hi=. Both bounds are floats. The function
takes the top 53 bits of the next output as u in [0, 1), and maps it to lo +
u * (hi - lo). When hi - lo is too large for a float, it uses lo * (1 - u) +
hi * u. A result that rounds up to hi is drawn again.
Call, on (random/seeded 42) |
Result |
|---|---|
(random/float .g 0.0 1.0) |
0.08386297105988216 |
(random/float .g -2.5 2.5) |
-2.080685144700589 |
(random/float .g 0 1) |
#err <mismatch type> |
(let [g (random/seeded 42)] [(random/float .g 0.0 1.0) (random/float .g 0.0 1.0) (random/float .g 0.0 1.0)]) (let [g (random/seeded 42)] [(random/float .g -2.5 2.5) (random/float .g 100.0 100.5) (random/float .g -1.0e308 1.0e308)]) (let [g (random/seeded 7)] [(random/float .g 0.0 1.0e-300) (random/float .g -1.0 0.0)])
(let [g (random/seeded 42)] [(random/float .g 0.0 1.0) (random/float .g 0.0 1.0) (random/float .g 0.0 1.0)]) (let [g (random/seeded 42)] [(random/float .g -2.5 2.5) (random/float .g 100.0 100.5) (random/float .g -1e+308 1e+308)]) (let [g (random/seeded 7)] [(random/float .g 0.0 1e-300) (random/float .g -1.0 0.0)])
[[0.08386297105988216 0.3789802506626686 0.6800434110281394] [-2.080685144700589 100.18949012533133 3.600868220562787e+307] [7.0057648217968965e-301 -0.7212487705262157]]
(random/float (random/seeded 1) 1.0 1.0) (random/float (random/seeded 1) 2.0 1.0)
#err {<invalid value> message "random range is empty: lo must be below hi"} #err {<invalid value> message "random range is empty: lo must be below hi"}
(random/float (random/seeded 1) 0 1) (random/float (random/seeded 1) 0.0 1) (random/float (random/seeded 1) a b)
#err {<mismatch type> message "random/float requires float bounds"} #err {<mismatch type> message "random/float requires float bounds"} #err {<mismatch type> message "random/float requires float bounds"}
8.5. random/choose ↑
Function(random/choose generator list) → value
Returns one item of list, each with the same chance. An empty list or
null produces #err <invalid value>.
Call, on (random/seeded 42) |
Result |
|---|---|
(random/choose .g [a b c]) |
a |
(random/choose .g [only]) |
only |
(random/choose .g []) |
#err <invalid value> |
(let [g (random/seeded 42)] [(random/choose .g [a b c]) (random/choose .g [a b c]) (random/choose .g [a b c]) (random/choose .g [a b c])]) (let [g (random/seeded 42)] [(random/choose .g [only]) (random/choose .g [1 2.5 "three" {four 4}])]) (let [g (random/seeded 7)] (random/choose .g [[1 2] [3 4]]))
(let [g (random/seeded 42)] [(random/choose .g [a b c]) (random/choose .g [a b c]) (random/choose .g [a b c]) (random/choose .g [a b c])]) (let [g (random/seeded 42)] [(random/choose .g [only]) (random/choose .g [1 2.5 three {four 4}])]) (let [g (random/seeded 7)] (random/choose .g [[1 2] [3 4]]))
[[a b c c] [only 2.5] [3 4]]
(random/choose (random/seeded 1) []) (random/choose (random/seeded 1) null)
#err {<invalid value> message "random/choose requires a nonempty list"} #err {<invalid value> message "random/choose requires a nonempty list"}
(random/choose (random/seeded 1) {a 1}) (random/choose (random/seeded 1) abc) (random/choose [a b])
#err {<mismatch type> message "expected a list or null"} #err {<mismatch type> message "expected a list or null"} #err {<arity mismatch> message "wrong number of arguments"}
8.6. random/shuffle ↑
Function(random/shuffle generator list) → list
Returns a new list with the items of list in a uniform random order, and
leaves list unchanged. The order comes from a Fisher–Yates shuffle that
swaps each position i, from the last down to the second, with a position
from 0 to i. null shuffles to [].
Call, on (random/seeded 7) |
Result |
|---|---|
(random/shuffle .g [1 2 3 4 5]) |
[1 5 3 2 4] |
(random/shuffle .g []) |
[] |
(random/shuffle .g {a 1}) |
#err <mismatch type> |
(let [g (random/seeded 7)] [(random/shuffle .g [1 2 3 4 5]) (random/shuffle .g [1 2 3 4 5]) (random/shuffle .g [a b c d e f g h])]) (let [g (random/seeded 7) xs [1 2 3]] [(random/shuffle .g .xs) .xs]) (let [g (random/seeded 7)] [(random/shuffle .g []) (random/shuffle .g [only]) (random/shuffle .g null)])
(let [g (random/seeded 7)] [(random/shuffle .g [1 2 3 4 5]) (random/shuffle .g [1 2 3 4 5]) (random/shuffle .g [a b c d e f g h])]) (let [g (random/seeded 7) xs [1 2 3]] [(random/shuffle .g .xs) .xs]) (let [g (random/seeded 7)] [(random/shuffle .g []) (random/shuffle .g [only]) (random/shuffle .g null)])
[[[1 5 3 2 4] [2 3 1 4 5] [g a c e f h b d]] [[2 1 3] [1 2 3]] [[] [only] []]]
(random/shuffle (random/seeded 1) {a 1}) (random/shuffle (random/seeded 1) abc) (random/shuffle [1 2 3])
#err {<mismatch type> message "expected a list or null"} #err {<mismatch type> message "expected a list or null"} #err {<arity mismatch> message "wrong number of arguments"}