std/random

  Source   Edit

Nim's standard random number generator (RNG).

Its implementation is based on the xoroshiro128+ (xor/rotate/shift/rotate) library.

Do not use this module for cryptographic purposes!

Basic usage

Example:

import std/random
# Call randomize() once to initialize the default random number generator.
# If this is not called, the same results will occur every time these
# examples are run.
randomize()

# Pick a number in 0..100.
let num = rand(100)
doAssert num in 0..100

# Roll a six-sided die.
let roll = rand(1..6)
doAssert roll in 1..6

# Pick a marble from a bag.
let marbles = ["red", "blue", "green", "yellow", "purple"]
let pick = sample(marbles)
doAssert pick in marbles

# Shuffle some cards.
var cards = ["Ace", "King", "Queen", "Jack", "Ten"]
shuffle(cards)
doAssert cards.len == 5

These examples all use the default RNG. The Rand type represents the state of an RNG. For convenience, this module contains a default Rand state that corresponds to the default RNG. Most procs in this module which do not take in a Rand parameter, including those called in the above examples, use the default generator. Those procs are not thread-safe.

Note that the default generator always starts in the same state. The randomize proc can be called to initialize the default generator with a seed based on the current time, and it only needs to be called once before the first usage of procs from this module. If randomize is not called, the default generator will always produce the same results.

RNGs that are independent of the default one can be created with the initRand proc.

Again, it is important to remember that this module must not be used for cryptographic applications.

See also

Types

Rand = object
  a0, a1: Ui

State of a random number generator.

Create a new Rand state using the initRand proc.

The module contains a default Rand state for convenience. It corresponds to the default RNG's state. The default Rand state always starts with the same values, but the randomize proc can be used to seed the default generator with a value based on the current time.

Many procs have two variations: one that takes in a Rand parameter and another that uses the default generator. The procs that use the default generator are not thread-safe!

  Source   Edit

Procs

proc gauss(mu = 0.0; sigma = 1.0): float {....raises: [], tags: [].}

Returns a Gaussian random variate, with mean mu and standard deviation sigma.

If randomize has not been called, the order of outcomes from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

  Source   Edit
proc gauss(r: var Rand; mu = 0.0; sigma = 1.0): float {....raises: [], tags: [].}
Returns a Gaussian random variate, with mean mu and standard deviation sigma using the given state.   Source   Edit
proc initRand(): Rand {....raises: [], tags: [].}

Initializes a new Rand state.

The resulting state is independent of the default RNG's state.

Note: Does not work for the compile-time VM.

See also:

  Source   Edit
proc initRand(seed: int64): Rand {....raises: [], tags: [].}

Initializes a new Rand state using the given seed.

Providing a specific seed will produce the same results for that seed each time.

The resulting state is independent of the default RNG's state. When seed == 0, we internally set the seed to an implementation defined non-zero value.

See also:

Example:

from std/times import getTime, toUnix, nanosecond

var r1 = initRand(123)
let now = getTime()
var r2 = initRand(now.toUnix * 1_000_000_000 + now.nanosecond)
  Source   Edit
proc initRand(seed: int64): Rand {....raises: [], tags: [].}

Initializes a new Rand state using the given seed.

Providing a specific seed will produce the same results for that seed each time.

The resulting state is independent of the default RNG's state. When seed == 0, we internally set the seed to an implementation defined non-zero value.

See also:

Example:

from std/times import getTime, toUnix, nanosecond

var r1 = initRand(123)
let now = getTime()
var r2 = initRand(now.toUnix * 1_000_000_000 + now.nanosecond)
  Source   Edit
proc next(r: var Rand): uint64 {....raises: [], tags: [].}

Computes a random uint64 number using the given state.

See also:

Example: cmd: -r:off

var r = initRand(2019)
assert r.next() == 13223559681708962501'u64 # implementation defined
assert r.next() == 7229677234260823147'u64 # ditto
  Source   Edit
proc rand(max: float): float {....gcsafe, locks: 0, ...raises: [], tags: [].}

Returns a random floating point number in the range 0.0..max.

If randomize has not been called, the sequence of random numbers returned from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

See also:

Example:

randomize(234)
let f = rand(1.0) # 8.717181376738381e-07
  Source   Edit
proc rand(max: int): int {....gcsafe, locks: 0, ...raises: [], tags: [].}

Returns a random integer in the range 0..max.

If randomize has not been called, the sequence of random numbers returned from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

See also:

Example: cmd: -r:off

randomize(123)
assert [rand(100), rand(100)] == [96, 63] # implementation defined
  Source   Edit
proc rand(r: var Rand; max: Natural): int {....gcsafe, locks: 0, ...raises: [],
    tags: [].}

Returns a random integer in the range 0..max using the given state.

See also:

Example:

var r = initRand(123)
if false:
  assert r.rand(100) == 96 # implementation defined
  Source   Edit
proc rand(r: var Rand; max: range[0.0 .. high(float)]): float {....gcsafe,
    locks: 0, ...raises: [], tags: [].}

Returns a random floating point number in the range 0.0..max using the given state.

See also:

Example:

var r = initRand(234)
let f = r.rand(1.0) # 8.717181376738381e-07
  Source   Edit
proc rand[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T

For a slice a..b, returns a value in the range a..b using the given state.

Allowed types for T are integers, floats, and enums without holes.

See also:

Example:

var r = initRand(345)
assert r.rand(1..5) <= 5
assert r.rand(-1.1 .. 1.2) >= -1.1
  Source   Edit
proc rand[T: Ordinal or SomeFloat](x: HSlice[T, T]): T

For a slice a..b, returns a value in the range a..b.

Allowed types for T are integers, floats, and enums without holes.

If randomize has not been called, the sequence of random numbers returned from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

See also:

  • rand proc that accepts a slice and uses a provided state
  • rand proc that returns an integer
  • rand proc that returns a floating point number
  • rand proc that accepts an integer or range type

Example:

randomize(345)
assert rand(1..6) <= 6
  Source   Edit
proc rand[T: SomeInteger](t: typedesc[T]): T

Returns a random integer in the range low(T)..high(T).

If randomize has not been called, the sequence of random numbers returned from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

See also:

Example:

randomize(567)
if false: # implementation defined
  assert rand(int8) == -42
  assert rand(uint32) == 578980729'u32
  assert rand(range[1..16]) == 11
  Source   Edit
proc randomize() {....gcsafe, locks: 0, ...raises: [], tags: [].}

Initializes the default random number generator with a seed based on random number source.

This proc only needs to be called once, and it should be called before the first usage of procs from this module that use the default RNG.

Note: Does not work for the compile-time VM.

See also:

  Source   Edit
proc randomize(seed: int64) {....gcsafe, locks: 0, ...raises: [], tags: [].}

Initializes the default random number generator with the given seed.

Providing a specific seed will produce the same results for that seed each time.

See also:

Example:

from std/times import getTime, toUnix, nanosecond

randomize(123)

let now = getTime()
randomize(now.toUnix * 1_000_000_000 + now.nanosecond)
  Source   Edit
proc sample[T, U](a: openArray[T]; cdf: openArray[U]): T

Returns an element from a using a cumulative distribution function (CDF).

This proc works similarly to sample. See that proc's documentation for more details.

If randomize has not been called, the order of outcomes from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

See also:

Example:

from std/math import cumsummed

let marbles = ["red", "blue", "green", "yellow", "purple"]
let count = [1, 6, 8, 3, 4]
let cdf = count.cumsummed
randomize(789)
assert sample(marbles, cdf) in marbles
  Source   Edit
proc sample[T, U](r: var Rand; a: openArray[T]; cdf: openArray[U]): T

Returns an element from a using a cumulative distribution function (CDF) and the given state.

The cdf argument does not have to be normalized, and it could contain any type of elements that can be converted to a float. It must be the same length as a. Each element in cdf should be greater than or equal to the previous element.

The outcome of the cumsum proc and the return value of the cumsummed proc, which are both in the math module, can be used as the cdf argument.

See also:

Example:

from std/math import cumsummed

let marbles = ["red", "blue", "green", "yellow", "purple"]
let count = [1, 6, 8, 3, 4]
let cdf = count.cumsummed
var r = initRand(789)
assert r.sample(marbles, cdf) in marbles
  Source   Edit
proc sample[T](a: openArray[T]): lent T

Returns a random element from a.

If randomize has not been called, the order of outcomes from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

See also:

Example:

let marbles = ["red", "blue", "green", "yellow", "purple"]
randomize(456)
assert sample(marbles) in marbles
  Source   Edit
proc sample[T](r: var Rand; a: openArray[T]): T

Returns a random element from a using the given state.

See also:

Example:

let marbles = ["red", "blue", "green", "yellow", "purple"]
var r = initRand(456)
assert r.sample(marbles) in marbles
  Source   Edit
proc sample[T](r: var Rand; s: set[T]): T

Returns a random element from the set s using the given state.

See also:

Example:

var r = initRand(987)
let s = {1, 3, 5, 7, 9}
assert r.sample(s) in s
  Source   Edit
proc sample[T](s: set[T]): T

Returns a random element from the set s.

If randomize has not been called, the order of outcomes from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

See also:

Example:

randomize(987)
let s = {1, 3, 5, 7, 9}
assert sample(s) in s
  Source   Edit
proc shuffle[T](r: var Rand; x: var openArray[T])

Shuffles a sequence of elements in-place using the given state.

See also:

Example:

var cards = ["Ace", "King", "Queen", "Jack", "Ten"]
var r = initRand(678)
r.shuffle(cards)
import std/algorithm
assert cards.sorted == @["Ace", "Jack", "King", "Queen", "Ten"]
  Source   Edit
proc shuffle[T](x: var openArray[T])

Shuffles a sequence of elements in-place.

If randomize has not been called, the order of outcomes from this proc will always be the same.

This proc uses the default RNG. Thus, it is not thread-safe.

See also:

Example:

var cards = ["Ace", "King", "Queen", "Jack", "Ten"]
randomize(678)
shuffle(cards)
import std/algorithm
assert cards.sorted == @["Ace", "Jack", "King", "Queen", "Ten"]
  Source   Edit
proc skipRandomNumbers(s: var Rand) {....raises: [], tags: [].}

The jump function for the generator.

This proc is equivalent to 2^64 calls to next, and it can be used to generate 2^64 non-overlapping subsequences for parallel computations.

When multiple threads are generating random numbers, each thread must own the Rand state it is using so that the thread can safely obtain random numbers. However, if each thread creates its own Rand state, the subsequences of random numbers that each thread generates may overlap, even if the provided seeds are unique. This is more likely to happen as the number of threads and amount of random numbers generated increases.

If many threads will generate random numbers concurrently, it is better to create a single Rand state and pass it to each thread. After passing the Rand state to a thread, call this proc before passing it to the next one. By using the Rand state this way, the subsequences of random numbers generated in each thread will never overlap as long as no thread generates more than 2^64 random numbers.

See also:

Example: cmd: --threads:on

import std/[random, threadpool]

const spawns = 4
const numbers = 100000

proc randomSum(r: Rand): int =
  var r = r
  for i in 1..numbers:
    result += r.rand(0..10)

var r = initRand(2019)
var vals: array[spawns, FlowVar[int]]
for val in vals.mitems:
  val = spawn randomSum(r)
  r.skipRandomNumbers()

for val in vals:
  doAssert abs(^val - numbers * 5) / numbers < 0.1
  Source   Edit

Templates

template randState(): untyped
Makes the default Rand state accessible from other modules. Useful for module authors.   Source   Edit