std/random/lib
std/random/src/lib.trb
Random, a seedable, splittable generator of random numbers that is a value: the same seed makes the same numbers
on every machine and in both back ends, and a generator handed to a function is how that function gets its
randomness (docs/design/RANDOM.md).
The algorithm is SplitMix64, and it is a promise: the sequence of a seed, the mapping from its bits to integers and
floats, the shuffle and the split are documented, frozen and pinned by known-answer tests, so numbers recorded with
one version of the toolchain are the numbers of every later one. Ordinary TorbScript over UInt64 and its wrapping
arithmetic, with no native and no capability: a package that makes random values from a generator it was handed
needs no permission. The operating system's source of unpredictable bytes is Entropy of std/os/entropy, and its
Entropy.int64() is where a seed that nobody can repeat comes from: Random.seeded Entropy.int64().
Not for secrets. SplitMix64 is fast and passes the statistical batteries, and a generator that is seeded with 64 bits
can be recovered from a few of its outputs. A key, a nonce or a token is made from Entropy.
Examples
var source = Random.seeded 42
const roll = source.int(1..=6)
print "{roll}"
var again = Random.seeded 42
print(again.int(1..=6) == roll)
// prints true
Related
Random.split- an independent generator for each part of a parallel computation.
type Random
type Random
A pseudo-random generator: SplitMix64, a 64-bit state that advances by an odd increment, its gamma, on every draw
and is mixed into the number the draw answers. Two generators with the same state and gamma make the same numbers,
which is the point; a copy of a generator repeats the numbers of the original, so a var is what draws.
A generator is made from a seed with Random.seeded, and a part of a computation that needs numbers of its own gets
one from Random.split instead of sharing: the numbers then do not depend on the order the parts run in.
Examples
var source = Random.seeded 7
const first = source.uint64()
var copy = source
print(copy.uint64() == source.uint64())
// prints true
print(first != source.uint64())
// prints true
Pitfalls
- Not for cryptography; see the module comment.
- Drawing changes the generator, so the receiver has to be a
var:var source = Random.seeded 42.
Related
Random.seeded- the constructor.Random.split- independent generators for parallel work.
fn seeded
static fn seeded(seed: Int): Self
The generator of seed: the state is the seed's 64 bits (the two's complement of a negative seed) and the gamma
is the golden gamma, 0x9e3779b97f4a7c15. The same seed makes the same sequence on every machine.
Examples
var source = Random.seeded 1234567
print source.uint64()
// prints 6457827717110365317
fn uint64
var fn uint64(): UInt64
The next 64 random bits as a UInt64, the output of SplitMix64 and the source of every other draw: the state
advances by the gamma, and the new state goes through the mixing function of the reference implementation
(z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9, z = (z ^ (z >> 27)) * 0x94d049bb133111eb, z ^ (z >> 31)). The
sequence of Random.seeded 1234567 begins 6457827717110365317, 3203168211198807973, 9817491932198370423.
Examples
var source = Random.seeded 0
print source.uint64()
// prints 16294208416658607535
fn float
var fn float(): Float64
A float in [0, 1): the top 53 bits of Random.uint64 (uint64() >> 11) divided by 2^53. Every answer is a
multiple of 2^-53, each of them equally likely, and the answer is never 1.0.
Examples
var source = Random.seeded 42
const chance = source.float()
print(chance >= 0.0 && chance < 1.0)
// prints true
fn int
var fn int(range: Range<Int>): Int
A whole number of range, every one equally likely: source.int(1..=6) is a die and
source.int(0..items.length()) an index. The draw is unbiased - it rejects the outputs of Random.uint64 that
would make some numbers likelier than others instead of taking a remainder of all of them: with count numbers in
the range and limit the remainder of 2^64 divided by count, the first output of at least limit answers
range.start plus its remainder by count. A range that covers all 2^64 numbers answers range.start plus an
output.
Examples
var source = Random.seeded 42
const die = source.int(1..=6)
print(die >= 1 && die <= 6)
// prints true
Panics
Where the range holds no number: 5..5, or 6..=5. A range of one number is fine, and answers it.
fn split
var fn split(): Self
A generator of its own, independent of this one for any practical purpose: it takes the next output as its state and a gamma mixed from the following state, and this generator moves on by two draws. Hand a split to each part of a parallel computation, and the numbers of every part stay the same whichever part runs first.
The gamma is the one of Java's SplittableRandom: the state mixed by MurmurHash3's finalizer, made odd, and
flipped by 0xaaaaaaaaaaaaaaaa when it has fewer than 24 bit transitions, so that it is not a weak increment. That
two splits are independent is an empirical statement, as it is for every splittable generator of this family.
Examples
var source = Random.seeded 42
var left = source.split()
var right = source.split()
print(left.uint64() != right.uint64())
// prints true
fn shuffled
var fn shuffled<Item>(items: List<Item>): List<Item>
The items in a random order, the original untouched. The order is Fisher-Yates from the end: for each position
index from the last down to 1, source.int(0..=index) picks the item that swaps into it.
Examples
var source = Random.seeded 42
const order = source.shuffled([1, 2, 3, 4, 5])
print order.length()
// prints 5
print(source.float() < 1.0)
// prints true
fn shuffle
var fn shuffle<Item>(var items: List<Item>)
Puts items in a random order, in place, with the Fisher-Yates shuffle of Random.shuffled.
Examples
var source = Random.seeded 42
var cards = ["ace", "king", "queen"]
source.shuffle cards
print cards.length()
// prints 3
print(source.choice(cards) != None)
// prints true
fn choice
var fn choice<Item>(items: List<Item>): Item?
One item of items, every one equally likely, or None where there is none: the item at int(0..items.length()).
Examples
var source = Random.seeded 42
const nobody: List<String> = []
print(source.choice(nobody) == None)
// prints true
print(source.choice(["only"]) == Some("only"))
// prints true
fn sample
var fn sample<Item>(items: List<Item>, count: Int): List<Item>
count different positions of items and the items at them, in a random order: the first count positions of a
shuffle that stops early. For each position index from 0 up to count, source.int(index..items.length()) picks
the item that swaps into it. A sample of every item is a shuffle, and an item that is in items twice can be in
the sample twice.
Examples
var source = Random.seeded 42
const hand = source.sample([1, 2, 3, 4, 5], 2)
print hand.length()
// prints 2
Panics
Where count is negative or more than items.length(): there are not that many different items to take.