std.rand
Reed's standard library. Imported with use std.rand; not on disk.
Functions
func random_seed(seed: i32) -> &Random
A generator seeded from a single value.
The seed is run through SplitMix64 to fill all four state words, because xorshift128 recovers slowly from a state that is mostly zeros: seeding it with (seed, 0, 0, 0) gives visibly poor output for the first few dozen draws. Every seed, including 0, produces a distinct well-distributed stream.
The same seed always produces the same sequence, on every platform.
- Parameters
- seed — the starting value
- Returns
- a new generator
Source
func random_seed(seed: i32) -> &Random {
let mixer: &SplitMix = new SplitMix { state: seed as i64.s };
let a: i64 = mixer.next();
let b: i64 = mixer.next();
return new Random {
x: wasm.i32.wrap_i64(a),
y: wasm.i32.wrap_i64(a >>> 32),
z: wasm.i32.wrap_i64(b),
w: wasm.i32.wrap_i64(b >>> 32),
};
}
Methods
func SplitMix.next(self: &SplitMix) -> i64
The next 64 bits of a SplitMix64 stream.
A complete generator in its own right, and the standard way to seed a larger one. Each call advances the state by a fixed odd increment and then mixes, so consecutive seeds give unrelated outputs -- the property random_seed needs.
- Returns
- the next value
Source
func SplitMix.next(self: &SplitMix) -> i64 {
self->state = self->state + 0x9E3779B97F4A7C15;
var z: i64 = self->state;
z = (z ^ (z >>> 30)) * 0xBF58476D1CE4E5B9;
z = (z ^ (z >>> 27)) * 0x94D049BB133111EB;
return z ^ (z >>> 31);
}
func Random.next(self: &Random) -> i32
The next 32 pseudorandom bits, uniformly distributed over the whole i32 range.
Every other method here is built on this one. The result is as likely to be negative as positive; use below or between for a bounded value.
- Returns
- the next value
- See also
Random.below, Random.between
Source
func Random.next(self: &Random) -> i32 {
let t: i32 = self->x ^ (self->x << 11);
self->x = self->y;
self->y = self->z;
self->z = self->w;
self->w = (self->w ^ (self->w >>> 19)) ^ (t ^ (t >>> 8));
return self->w;
}
func Random.next_i64(self: &Random) -> i64
The next 64 pseudorandom bits.
Two 32-bit draws combined, so it advances the generator twice.
- Returns
- the next value
Source
func Random.next_i64(self: &Random) -> i64 {
let hi: i64 = self.next() as i64.u;
let lo: i64 = self.next() as i64.u;
return (hi << 32) | lo;
}
func Random.below(self: &Random, bound: i32) -> i32
A uniformly distributed value in 0..bound.
Returns 0 when bound is zero or negative rather than trapping.
Uniform, not merely bounded. The naive next() % bound is biased whenever bound does not divide 2^32: the low residues occur once more often than the high ones, by up to a factor of bound / 2^32. This rejects the unbalanced tail of the range instead, so every value in 0..bound is exactly equally likely. The rejection loop is expected to run barely more than once (at most twice on average, and only for a bound above 2^31).
- Parameters
- bound — the exclusive upper bound
- Returns
- a value in
0..bound
- See also
Random.between
Source
func Random.below(self: &Random, bound: i32) -> i32 {
if (bound <= 0) { return 0; }
let limit: i32 = 0 - ((0 - bound).rem_u(bound));
if (limit == 0) { return self.next().rem_u(bound); }
loop draw() {
let value: i32 = self.next();
if (value.lt_u(limit)) { return value.rem_u(bound); }
continue draw();
}
}
func Random.between(self: &Random, lo: i32, hi: i32) -> i32
A uniformly distributed value in lo..hi.
Returns lo when the range is empty. A range spanning more than 2^31 values is handled correctly: the width is computed as unsigned, so between(-2147483648, 2147483647) works rather than overflowing.
- Parameters
- lo — the inclusive lower bound
- hi — the exclusive upper bound
- Returns
- a value in
lo..hi
- See also
Random.below
Source
func Random.between(self: &Random, lo: i32, hi: i32) -> i32 {
if (hi <= lo) { return lo; }
return lo + self.below(hi - lo);
}
func Random.boolean(self: &Random) -> i32
A pseudorandom boolean.
Reads the high bit, not the low one: xorshift's low bits are its weakest, and next() & 1 is a visibly worse coin than next() < 0.
- Returns
0 or 1, each with probability one half
Source
func Random.boolean(self: &Random) -> i32 {
return (self.next() >>> 31) & 1;
}
func Random.unit(self: &Random) -> f64
A pseudorandom f64 in [0, 1).
Built from 53 random bits, which is exactly the mantissa width of an f64, so every representable value in the interval is reachable and the distribution is uniform. The common next() / 2^32 formulation reaches only 2^32 of them.
- Returns
- a value in
[0, 1)
Source
func Random.unit(self: &Random) -> f64 {
let bits: i64 = self.next_i64() >>> 11;
return (bits as f64.u) * 1.1102230246251565e-16;
}
func Random.shuffle(self: &Random, values: &I32Values) -> void
Shuffles values in place, uniformly.
The Fisher-Yates shuffle: every one of the n! orderings is equally likely, assuming the generator is. Note the loop runs downward and picks from 0..=i inclusive -- the common "pick from the whole array each time" variant is not uniform and quietly favours some orderings over others.
- Parameters
- values — the array to shuffle
Source
func Random.shuffle(self: &Random, values: &I32Values) {
var i: i32 = #values;
loop swap() {
if (i <= 1) { break swap(); }
i = i - 1;
let j: i32 = self.below(i + 1);
let t: i32 = values[i];
values[i] = values[j];
values[j] = t;
continue swap();
}
}
func Random.choice(self: &Random, values: &I32Values) -> (i32, i32)
A uniformly chosen element of values.
Returns (0, 0) for an empty array; the flag distinguishes that from having drawn a stored zero.
- Parameters
- values — the array to choose from
- Returns
(value, ok)
Source
func Random.choice(self: &Random, values: &I32Values) -> (i32, i32) {
if (#values == 0) { return (0, 0); }
return (values[self.below(#values)], 1);
}
Types
| Random | A xorshift128 pseudorandom generator. |
| SplitMix | A SplitMix64 state, for expanding one seed into many well-distributed values. |
struct Random {
x: mut i32,
y: mut i32,
z: mut i32,
w: mut i32,
}
A xorshift128 pseudorandom generator. Create one with random_seed.
Source
struct Random {
x: mut i32,
y: mut i32,
z: mut i32,
w: mut i32,
}
struct SplitMix {
state: mut i64,
}
A SplitMix64 state, for expanding one seed into many well-distributed values.
Source
struct SplitMix {
state: mut i64,
}