From c4be10116db46c60442de707739209bd97bed1b8 Mon Sep 17 00:00:00 2001 From: konsumlamm Date: Thu, 11 Feb 2021 21:50:39 +0100 Subject: [PATCH 1/3] Improve documentation for random Use runnableExamples Minor changes --- lib/pure/random.nim | 266 ++++++++++++++++++++++---------------------- 1 file changed, 135 insertions(+), 131 deletions(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index f03cae56a0b3f..7a351d4dc4ada 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -7,9 +7,9 @@ # distribution, for details about the copyright. # -## Nim's standard random number generator. +## Nim's standard random number generator (RNG). ## -## Its implementation is based on the ``xoroshiro128+`` +## Its implementation is based on the `xoroshiro128+` ## (xor/rotate/shift/rotate) library. ## * More information: http://xoroshiro.di.unimi.it ## * C implementation: http://xoroshiro.di.unimi.it/xoroshiro128plus.c @@ -19,58 +19,52 @@ ## Basic usage ## =========== ## -## To get started, here are some examples: -## -## .. code-block:: -## -## import 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 between 0 and 100 -## let num = rand(100) -## echo num -## -## # Roll a six-sided die -## let roll = rand(1..6) -## echo roll -## -## # Pick a marble from a bag -## let marbles = ["red", "blue", "green", "yellow", "purple"] -## let pick = sample(marbles) -## echo pick -## -## # Shuffle some cards -## var cards = ["Ace", "King", "Queen", "Jack", "Ten"] -## shuffle(cards) -## echo cards -## +runnableExamples: + # 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 between 0 and 100. + let num = rand(100) + echo num + + # Roll a six-sided die. + let roll = rand(1..6) + echo roll + + # Pick a marble from a bag. + let marbles = ["red", "blue", "green", "yellow", "purple"] + let pick = sample(marbles) + echo pick + + # Shuffle some cards. + var cards = ["Ace", "King", "Queen", "Jack", "Ten"] + shuffle(cards) + echo cards + ## These examples all use the default random number generator. The -## `Rand type<#Rand>`_ represents the state of a random number generator. +## `Rand type <#Rand>`_ represents the state of a random number generator. ## For convenience, this module contains a default Rand state that corresponds ## to the default random number generator. 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<#randomize>`_ can be called to initialize the default +## The `randomize proc <#randomize>`_ 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, then the default generator will always produce +## `randomize` is not called, the default generator will always produce ## the same results. ## -## Generators that are independent of the default one can be created with the -## `initRand proc<#initRand,int64>`_. +## RNGs that are independent of the default one can be created with the +## `initRand proc <#initRand,int64>`_. ## ## Again, it is important to remember that this module must **not** be used for ## cryptographic applications. ## ## See also ## ======== -## * `math module`_ for basic math routines ## * `mersenne module`_ for the Mersenne Twister random number ## generator ## * `stats module`_ for statistical analysis @@ -78,10 +72,10 @@ ## `_ ## in the standard library -import algorithm, math +import std/[algorithm, math] import std/private/since -include "system/inclrtl" +include system/inclrtl {.push debugger: off.} when defined(js): @@ -96,12 +90,12 @@ else: type Rand* = object ## State of a random number generator. ## - ## Create a new Rand state using the `initRand proc<#initRand,int64>`_. + ## Create a new Rand state using the `initRand proc <#initRand,int64>`_. ## ## The module contains a default Rand state for convenience. ## It corresponds to the default random number generator's state. ## The default Rand state always starts with the same values, but the - ## `randomize proc<#randomize>`_ can be used to seed the default generator + ## `randomize proc <#randomize>`_ 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 @@ -129,9 +123,9 @@ proc rotl(x, k: Ui): Ui = result = (x shl k) or (x shr (Ui(64) - k)) proc next*(r: var Rand): uint64 = - ## Computes a random ``uint64`` number using the given state. + ## Computes a random `uint64` number using the given state. ## - ## See also: + ## **See also:** ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer between zero and ## a given upper bound ## * `rand proc<#rand,Rand,range[]>`_ that returns a float @@ -144,6 +138,7 @@ proc next*(r: var Rand): uint64 = doAssert r.next() == 138_744_656_611_299'u64 doAssert r.next() == 979_810_537_855_049_344'u64 doAssert r.next() == 3_628_232_584_225_300_704'u64 + let s0 = r.a0 var s1 = r.a1 result = s0 + s1 @@ -154,12 +149,12 @@ proc next*(r: var Rand): uint64 = proc skipRandomNumbers*(s: var Rand) = ## The jump function for the generator. ## - ## This proc is equivalent to 2^64 calls to `next<#next,Rand>`_, and it can + ## This proc is equivalent to 2^64 calls to `next <#next,Rand>`_, 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<#Rand>`_ state it is using so that the thread can safely + ## own the `Rand <#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 @@ -172,32 +167,28 @@ proc skipRandomNumbers*(s: var Rand) = ## generated in each thread will never overlap as long as no thread generates ## more than 2^64 random numbers. ## - ## The following example below demonstrates this pattern: - ## - ## .. code-block:: - ## # Compile this example with --threads:on - ## import random - ## import threadpool - ## - ## const spawns = 4 - ## const numbers = 100000 - ## - ## proc randomSum(rand: Rand): int = - ## var r = rand - ## for i in 1..numbers: - ## result += rand(1..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: - ## echo ^val - ## - ## See also: + ## **See also:** ## * `next proc<#next,Rand>`_ + runnableExamples("--threads:on"): + import std/[random, threadpool] + + const spawns = 4 + const numbers = 100000 + + proc randomSum(rand: Rand): int = + var r = rand + for i in 1..numbers: + result += rand(1..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: + echo ^val + when defined(js): const helper = [0xbeac0467u32, 0xd86b048bu32] else: @@ -217,7 +208,7 @@ proc skipRandomNumbers*(s: var Rand) = proc rand*(r: var Rand; max: Natural): int {.benign.} = ## Returns a random integer in the range `0..max` using the given state. ## - ## See also: + ## **See also:** ## * `rand proc<#rand,int>`_ that returns an integer using the default ## random number generator ## * `rand proc<#rand,Rand,range[]>`_ that returns a float @@ -229,22 +220,23 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} = doAssert r.rand(100) == 0 doAssert r.rand(100) == 96 doAssert r.rand(100) == 66 + if max == 0: return while true: let x = next(r) if x <= randMax - (randMax mod Ui(max)): - return int(x mod (uint64(max)+1u64)) + return int(x mod (uint64(max) + 1u64)) proc rand*(max: int): int {.benign.} = ## Returns a random integer in the range `0..max`. ## - ## If `randomize<#randomize>`_ has not been called, the sequence of random + ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** ## thread-safe. ## - ## See also: + ## **See also:** ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a ## provided state ## * `rand proc<#rand,float>`_ that returns a float @@ -256,13 +248,14 @@ proc rand*(max: int): int {.benign.} = doAssert rand(100) == 0 doAssert rand(100) == 96 doAssert rand(100) == 66 + rand(state, max) proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = ## Returns a random floating point number in the range `0.0..max` ## using the given state. ## - ## See also: + ## **See also:** ## * `rand proc<#rand,float>`_ that returns a float using the default ## random number generator ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer @@ -271,8 +264,8 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: var r = initRand(234) - let f = r.rand(1.0) - ## f = 8.717181376738381e-07 + let f = r.rand(1.0) # 8.717181376738381e-07 + let x = next(r) when defined(js): result = (float(x) / float(high(uint32))) * max @@ -283,13 +276,13 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = proc rand*(max: float): float {.benign.} = ## Returns a random floating point number in the range `0.0..max`. ## - ## If `randomize<#randomize>`_ has not been called, the sequence of random + ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** ## thread-safe. ## - ## See also: + ## **See also:** ## * `rand proc<#rand,Rand,range[]>`_ that returns a float using a ## provided state ## * `rand proc<#rand,int>`_ that returns an integer @@ -298,8 +291,8 @@ proc rand*(max: float): float {.benign.} = ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: randomize(234) - let f = rand(1.0) - ## f = 8.717181376738381e-07 + let f = rand(1.0) # 8.717181376738381e-07 + rand(state, max) proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = @@ -308,7 +301,7 @@ proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = ## ## Allowed types for `T` are integers, floats, and enums without holes. ## - ## See also: + ## **See also:** ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice and uses the default random number generator ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer @@ -319,8 +312,8 @@ proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = doAssert r.rand(1..6) == 4 doAssert r.rand(1..6) == 4 doAssert r.rand(1..6) == 6 - let f = r.rand(-1.0 .. 1.0) - ## f = 0.8741183448756229 + let f = r.rand(-1.0 .. 1.0) # 0.8741183448756229 + when T is SomeFloat: result = rand(r, x.b - x.a) + x.a else: # Integers and Enum types @@ -331,13 +324,13 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = ## ## Allowed types for `T` are integers, floats, and enums without holes. ## - ## If `randomize<#randomize>`_ has not been called, the sequence of random + ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** ## thread-safe. ## - ## See also: + ## **See also:** ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice and uses a provided state ## * `rand proc<#rand,int>`_ that returns an integer @@ -348,18 +341,19 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = doAssert rand(1..6) == 4 doAssert rand(1..6) == 4 doAssert rand(1..6) == 6 + result = rand(state, x) proc rand*[T: SomeInteger](t: typedesc[T]): T = ## Returns a random integer in the range `low(T)..high(T)`. ## - ## If `randomize<#randomize>`_ has not been called, the sequence of random + ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** ## thread-safe. ## - ## See also: + ## **See also:** ## * `rand proc<#rand,int>`_ that returns an integer ## * `rand proc<#rand,float>`_ that returns a floating point number ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ @@ -375,18 +369,19 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T = doAssert rand(range[1..16]) == 11 doAssert rand(range[1..16]) == 4 doAssert rand(range[1..16]) == 16 + when T is range: result = rand(state, low(T)..high(T)) else: result = cast[T](state.next) proc sample*[T](r: var Rand; s: set[T]): T = - ## Returns a random element from the set ``s`` using the given state. + ## Returns a random element from the set `s` using the given state. ## - ## See also: + ## **See also:** ## * `sample proc<#sample,set[T]>`_ that uses the default random number ## generator - ## * `sample proc<#sample,Rand,openArray[T]>`_ for openarrays + ## * `sample proc<#sample,Rand,openArray[T]>`_ for `openArray`s ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function runnableExamples: @@ -395,6 +390,7 @@ proc sample*[T](r: var Rand; s: set[T]): T = doAssert r.sample(s) == 5 doAssert r.sample(s) == 7 doAssert r.sample(s) == 1 + assert card(s) != 0 var i = rand(r, card(s) - 1) for e in s: @@ -402,17 +398,17 @@ proc sample*[T](r: var Rand; s: set[T]): T = dec(i) proc sample*[T](s: set[T]): T = - ## Returns a random element from the set ``s``. + ## Returns a random element from the set `s`. ## - ## If `randomize<#randomize>`_ has not been called, the order of outcomes + ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** ## thread-safe. ## - ## See also: + ## **See also:** ## * `sample proc<#sample,Rand,set[T]>`_ that uses a provided state - ## * `sample proc<#sample,openArray[T]>`_ for openarrays + ## * `sample proc<#sample,openArray[T]>`_ for `openArray`s ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function runnableExamples: @@ -421,12 +417,13 @@ proc sample*[T](s: set[T]): T = doAssert sample(s) == 5 doAssert sample(s) == 7 doAssert sample(s) == 1 + sample(state, s) proc sample*[T](r: var Rand; a: openArray[T]): T = - ## Returns a random element from ``a`` using the given state. + ## Returns a random element from `a` using the given state. ## - ## See also: + ## **See also:** ## * `sample proc<#sample,openArray[T]>`_ that uses the default ## random number generator ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that uses a @@ -438,18 +435,19 @@ proc sample*[T](r: var Rand; a: openArray[T]): T = doAssert r.sample(marbles) == "blue" doAssert r.sample(marbles) == "yellow" doAssert r.sample(marbles) == "red" + result = a[r.rand(a.low..a.high)] proc sample*[T](a: openArray[T]): T = - ## Returns a random element from ``a``. + ## Returns a random element from `a`. ## - ## If `randomize<#randomize>`_ has not been called, the order of outcomes + ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** ## thread-safe. ## - ## See also: + ## **See also:** ## * `sample proc<#sample,Rand,openArray[T]>`_ that uses a provided state ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function @@ -460,28 +458,29 @@ proc sample*[T](a: openArray[T]): T = doAssert sample(marbles) == "blue" doAssert sample(marbles) == "yellow" doAssert sample(marbles) == "red" + result = a[rand(a.low..a.high)] proc sample*[T, U](r: var Rand; a: openArray[T]; cdf: openArray[U]): T = - ## Returns an element from ``a`` using a cumulative distribution function + ## 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 + ## 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. + ## which are both in the math module, can be used as the `cdf` argument. ## - ## See also: + ## **See also:** ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that also utilizes ## a CDF but uses the default random number generator ## * `sample proc<#sample,Rand,openArray[T]>`_ that does not use a CDF ## * `sample proc<#sample,Rand,set[T]>`_ for sets runnableExamples: - from math import cumsummed + from std/math import cumsummed let marbles = ["red", "blue", "green", "yellow", "purple"] let count = [1, 6, 8, 3, 4] @@ -490,35 +489,35 @@ proc sample*[T, U](r: var Rand; a: openArray[T]; cdf: openArray[U]): T = doAssert r.sample(marbles, cdf) == "red" doAssert r.sample(marbles, cdf) == "green" doAssert r.sample(marbles, cdf) == "blue" + assert(cdf.len == a.len) # Two basic sanity checks. assert(float(cdf[^1]) > 0.0) - #While we could check cdf[i-1] <= cdf[i] for i in 1..cdf.len, that could get - #awfully expensive even in debugging modes. + # While we could check cdf[i-1] <= cdf[i] for i in 1..cdf.len, that could get + # awfully expensive even in debugging modes. let u = r.rand(float(cdf[^1])) a[cdf.upperBound(U(u))] proc sample*[T, U](a: openArray[T]; cdf: openArray[U]): T = - ## Returns an element from ``a`` using a cumulative distribution function + ## Returns an element from `a` using a cumulative distribution function ## (CDF). ## ## This proc works similarly to - ## `sample[T, U](Rand, openArray[T], openArray[U]) - ## <#sample,Rand,openArray[T],openArray[U]>`_. + ## `sample <#sample,Rand,openArray[T],openArray[U]>`_. ## See that proc's documentation for more details. ## - ## If `randomize<#randomize>`_ has not been called, the order of outcomes + ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** ## thread-safe. ## - ## See also: + ## **See also:** ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that also utilizes ## a CDF but uses a provided state ## * `sample proc<#sample,openArray[T]>`_ that does not use a CDF ## * `sample proc<#sample,set[T]>`_ for sets runnableExamples: - from math import cumsummed + from std/math import cumsummed let marbles = ["red", "blue", "green", "yellow", "purple"] let count = [1, 6, 8, 3, 4] @@ -527,14 +526,15 @@ proc sample*[T, U](a: openArray[T]; cdf: openArray[U]): T = doAssert sample(marbles, cdf) == "red" doAssert sample(marbles, cdf) == "green" doAssert sample(marbles, cdf) == "blue" + state.sample(a, cdf) proc gauss*(r: var Rand; mu = 0.0; sigma = 1.0): float {.since: (1, 3).} = ## Returns a Gaussian random variate, - ## with mean ``mu`` and standard deviation ``sigma`` + ## with mean `mu` and standard deviation `sigma` ## using the given state. # Ratio of uniforms method for normal - # http://www2.econ.osaka-u.ac.jp/~tanizaki/class/2013/econome3/13.pdf + # https://www2.econ.osaka-u.ac.jp/~tanizaki/class/2013/econome3/13.pdf const K = sqrt(2 / E) var a = 0.0 @@ -547,9 +547,9 @@ proc gauss*(r: var Rand; mu = 0.0; sigma = 1.0): float {.since: (1, 3).} = proc gauss*(mu = 0.0, sigma = 1.0): float {.since: (1, 3).} = ## Returns a Gaussian random variate, - ## with mean ``mu`` and standard deviation ``sigma``. + ## with mean `mu` and standard deviation `sigma`. ## - ## If `randomize<#randomize>`_ has not been called, the order of outcomes + ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** @@ -557,7 +557,7 @@ proc gauss*(mu = 0.0, sigma = 1.0): float {.since: (1, 3).} = result = gauss(state, mu, sigma) proc initRand*(seed: int64): Rand = - ## Initializes a new `Rand<#Rand>`_ state using the given seed. + ## Initializes a new `Rand <#Rand>`_ state using the given seed. ## ## `seed` must not be zero. Providing a specific seed will produce ## the same results for that seed each time. @@ -565,19 +565,20 @@ proc initRand*(seed: int64): Rand = ## The resulting state is independent of the default random number ## generator's state. ## - ## See also: + ## **See also:** ## * `initRand proc<#initRand>`_ that uses the current time ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default ## random number generator ## * `randomize proc<#randomize>`_ that initializes the default random ## number generator using the current time runnableExamples: - from times import getTime, toUnix, nanosecond + 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) + doAssert seed != 0 # 0 causes `rand(int)` to always return 0 for example. result.a0 = Ui(seed shr 16) result.a1 = Ui(seed and 0xffff) @@ -589,25 +590,26 @@ proc randomize*(seed: int64) {.benign.} = ## `seed` must not be zero. Providing a specific seed will produce ## the same results for that seed each time. ## - ## See also: + ## **See also:** ## * `initRand proc<#initRand,int64>`_ that initializes a Rand state ## with a given seed ## * `randomize proc<#randomize>`_ that uses the current time instead ## * `initRand proc<#initRand>`_ that initializes a Rand state using ## the current time runnableExamples: - from times import getTime, toUnix, nanosecond + from std/times import getTime, toUnix, nanosecond randomize(123) let now = getTime() randomize(now.toUnix * 1_000_000_000 + now.nanosecond) + state = initRand(seed) proc shuffle*[T](r: var Rand; x: var openArray[T]) = ## Shuffles a sequence of elements in-place using the given state. ## - ## See also: + ## **See also:** ## * `shuffle proc<#shuffle,openArray[T]>`_ that uses the default ## random number generator runnableExamples: @@ -615,6 +617,7 @@ proc shuffle*[T](r: var Rand; x: var openArray[T]) = var r = initRand(678) r.shuffle(cards) doAssert cards == ["King", "Ace", "Queen", "Ten", "Jack"] + for i in countdown(x.high, 1): let j = r.rand(i) swap(x[i], x[j]) @@ -622,24 +625,25 @@ proc shuffle*[T](r: var Rand; x: var openArray[T]) = proc shuffle*[T](x: var openArray[T]) = ## Shuffles a sequence of elements in-place. ## - ## If `randomize<#randomize>`_ has not been called, the order of outcomes + ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## ## This proc uses the default random number generator. Thus, it is **not** ## thread-safe. ## - ## See also: + ## **See also:** ## * `shuffle proc<#shuffle,Rand,openArray[T]>`_ that uses a provided state runnableExamples: var cards = ["Ace", "King", "Queen", "Jack", "Ten"] randomize(678) shuffle(cards) doAssert cards == ["King", "Ace", "Queen", "Ten", "Jack"] + shuffle(state, x) when not defined(nimscript) and not defined(standalone): - import times - + import std/times + proc initRand(): Rand = ## Initializes a new Rand state with a seed based on the current time. ## @@ -659,7 +663,7 @@ when not defined(nimscript) and not defined(standalone): else: let now = times.getTime() result = initRand(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond) - + since (1, 5, 1): export initRand @@ -673,7 +677,7 @@ when not defined(nimscript) and not defined(standalone): ## ## **Note:** Does not work for NimScript or the compile-time VM. ## - ## See also: + ## **See also:** ## * `randomize proc<#randomize,int64>`_ that accepts a seed ## * `initRand proc<#initRand>`_ that initializes a Rand state using ## the current time From 2250a5e55bb2d09e809857bbca4a1835a3be0003 Mon Sep 17 00:00:00 2001 From: konsumlamm Date: Fri, 12 Feb 2021 01:00:00 +0100 Subject: [PATCH 2/3] Apply suggestions Remove echo Use RNG in more places --- lib/pure/random.nim | 94 ++++++++++++++++++--------------------------- 1 file changed, 37 insertions(+), 57 deletions(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 7a351d4dc4ada..53f4bb0423a34 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -25,28 +25,28 @@ runnableExamples: # examples are run. randomize() - # Pick a number between 0 and 100. + # Pick a number in 0..100. let num = rand(100) - echo num + doAssert num in 0..100 # Roll a six-sided die. let roll = rand(1..6) - echo roll + doAssert roll in 1..6 # Pick a marble from a bag. let marbles = ["red", "blue", "green", "yellow", "purple"] let pick = sample(marbles) - echo pick + doAssert pick in marbles # Shuffle some cards. var cards = ["Ace", "King", "Queen", "Jack", "Ten"] shuffle(cards) - echo cards + doAssert cards.len == 5 -## These examples all use the default random number generator. The -## `Rand type <#Rand>`_ represents the state of a random number generator. +## These examples all use the default RNG. The +## `Rand type <#Rand>`_ represents the state of an RNG. ## For convenience, this module contains a default Rand state that corresponds -## to the default random number generator. Most procs in this module which do +## 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. ## @@ -93,7 +93,7 @@ type ## Create a new Rand state using the `initRand proc <#initRand,int64>`_. ## ## The module contains a default Rand state for convenience. - ## It corresponds to the default random number generator's state. + ## It corresponds to the default RNG's state. ## The default Rand state always starts with the same values, but the ## `randomize proc <#randomize>`_ can be used to seed the default generator ## with a value based on the current time. @@ -149,8 +149,8 @@ proc next*(r: var Rand): uint64 = proc skipRandomNumbers*(s: var Rand) = ## The jump function for the generator. ## - ## This proc is equivalent to 2^64 calls to `next <#next,Rand>`_, and it can - ## be used to generate 2^64 non-overlapping subsequences for parallel + ## This proc is equivalent to `2^64` calls to `next <#next,Rand>`_, 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 @@ -165,7 +165,7 @@ proc skipRandomNumbers*(s: var Rand) = ## 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. + ## more than `2^64` random numbers. ## ## **See also:** ## * `next proc<#next,Rand>`_ @@ -178,7 +178,7 @@ proc skipRandomNumbers*(s: var Rand) = proc randomSum(rand: Rand): int = var r = rand for i in 1..numbers: - result += rand(1..10) + result += rand(0..10) var r = initRand(2019) var vals: array[spawns, FlowVar[int]] @@ -187,7 +187,7 @@ proc skipRandomNumbers*(s: var Rand) = r.skipRandomNumbers() for val in vals: - echo ^val + doAssert abs(^val - numbers * 5) / numbers < 0.1 when defined(js): const helper = [0xbeac0467u32, 0xd86b048bu32] @@ -209,8 +209,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} = ## Returns a random integer in the range `0..max` using the given state. ## ## **See also:** - ## * `rand proc<#rand,int>`_ that returns an integer using the default - ## random number generator + ## * `rand proc<#rand,int>`_ that returns an integer using the default RNG ## * `rand proc<#rand,Rand,range[]>`_ that returns a float ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice @@ -233,8 +232,7 @@ proc rand*(max: int): int {.benign.} = ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. ## ## **See also:** ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a @@ -256,8 +254,7 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = ## using the given state. ## ## **See also:** - ## * `rand proc<#rand,float>`_ that returns a float using the default - ## random number generator + ## * `rand proc<#rand,float>`_ that returns a float using the default RNG ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice @@ -279,8 +276,7 @@ proc rand*(max: float): float {.benign.} = ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. ## ## **See also:** ## * `rand proc<#rand,Rand,range[]>`_ that returns a float using a @@ -303,7 +299,7 @@ proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = ## ## **See also:** ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ - ## that accepts a slice and uses the default random number generator + ## that accepts a slice and uses the default RNG ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer ## * `rand proc<#rand,Rand,range[]>`_ that returns a float ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type @@ -327,8 +323,7 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. ## ## **See also:** ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ @@ -350,8 +345,7 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T = ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. ## ## **See also:** ## * `rand proc<#rand,int>`_ that returns an integer @@ -379,8 +373,7 @@ proc sample*[T](r: var Rand; s: set[T]): T = ## Returns a random element from the set `s` using the given state. ## ## **See also:** - ## * `sample proc<#sample,set[T]>`_ that uses the default random number - ## generator + ## * `sample proc<#sample,set[T]>`_ that uses the default RNG ## * `sample proc<#sample,Rand,openArray[T]>`_ for `openArray`s ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function @@ -403,8 +396,7 @@ proc sample*[T](s: set[T]): T = ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. ## ## **See also:** ## * `sample proc<#sample,Rand,set[T]>`_ that uses a provided state @@ -424,8 +416,7 @@ proc sample*[T](r: var Rand; a: openArray[T]): T = ## Returns a random element from `a` using the given state. ## ## **See also:** - ## * `sample proc<#sample,openArray[T]>`_ that uses the default - ## random number generator + ## * `sample proc<#sample,openArray[T]>`_ that uses the default RNG ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that uses a ## cumulative distribution function ## * `sample proc<#sample,Rand,set[T]>`_ for sets @@ -444,8 +435,7 @@ proc sample*[T](a: openArray[T]): T = ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. ## ## **See also:** ## * `sample proc<#sample,Rand,openArray[T]>`_ that uses a provided state @@ -476,7 +466,7 @@ proc sample*[T, U](r: var Rand; a: openArray[T]; cdf: openArray[U]): T = ## ## **See also:** ## * `sample proc<#sample,openArray[T],openArray[U]>`_ that also utilizes - ## a CDF but uses the default random number generator + ## a CDF but uses the default RNG ## * `sample proc<#sample,Rand,openArray[T]>`_ that does not use a CDF ## * `sample proc<#sample,Rand,set[T]>`_ for sets runnableExamples: @@ -508,8 +498,7 @@ proc sample*[T, U](a: openArray[T]; cdf: openArray[U]): T = ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. ## ## **See also:** ## * `sample proc<#sample,Rand,openArray[T],openArray[U]>`_ that also utilizes @@ -552,8 +541,7 @@ proc gauss*(mu = 0.0, sigma = 1.0): float {.since: (1, 3).} = ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. result = gauss(state, mu, sigma) proc initRand*(seed: int64): Rand = @@ -562,15 +550,12 @@ proc initRand*(seed: int64): Rand = ## `seed` must not be zero. Providing a specific seed will produce ## the same results for that seed each time. ## - ## The resulting state is independent of the default random number - ## generator's state. + ## The resulting state is independent of the default RNG's state. ## ## **See also:** ## * `initRand proc<#initRand>`_ that uses the current time - ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default - ## random number generator - ## * `randomize proc<#randomize>`_ that initializes the default random - ## number generator using the current time + ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default RNG + ## * `randomize proc<#randomize>`_ that initializes the default RNG using the current time runnableExamples: from std/times import getTime, toUnix, nanosecond @@ -610,8 +595,7 @@ proc shuffle*[T](r: var Rand; x: var openArray[T]) = ## Shuffles a sequence of elements in-place using the given state. ## ## **See also:** - ## * `shuffle proc<#shuffle,openArray[T]>`_ that uses the default - ## random number generator + ## * `shuffle proc<#shuffle,openArray[T]>`_ that uses the default RNG runnableExamples: var cards = ["Ace", "King", "Queen", "Jack", "Ten"] var r = initRand(678) @@ -628,8 +612,7 @@ proc shuffle*[T](x: var openArray[T]) = ## If `randomize <#randomize>`_ has not been called, the order of outcomes ## from this proc will always be the same. ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. + ## This proc uses the default RNG. Thus, it is **not** thread-safe. ## ## **See also:** ## * `shuffle proc<#shuffle,Rand,openArray[T]>`_ that uses a provided state @@ -647,16 +630,14 @@ when not defined(nimscript) and not defined(standalone): proc initRand(): Rand = ## Initializes a new Rand state with a seed based on the current time. ## - ## The resulting state is independent of the default random number generator's state. + ## The resulting state is independent of the default RNG's state. ## ## **Note:** Does not work for NimScript or the compile-time VM. ## ## See also: ## * `initRand proc<#initRand,int64>`_ that accepts a seed for a new Rand state - ## * `randomize proc<#randomize>`_ that initializes the default random - ## number generator using the current time - ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default - ## random number generator + ## * `randomize proc<#randomize>`_ that initializes the default RNG using the current time + ## * `randomize proc<#randomize,int64>`_ that accepts a seed for the default RNG when defined(js): let time = int64(times.epochTime() * 1000) and 0x7fff_ffff result = initRand(time) @@ -672,8 +653,7 @@ when not defined(nimscript) and not defined(standalone): ## the current time. ## ## 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 random - ## number generator. + ## the first usage of procs from this module that use the default RNG. ## ## **Note:** Does not work for NimScript or the compile-time VM. ## From 71199eedb8de06ee43783710cb4a1498729da2af Mon Sep 17 00:00:00 2001 From: konsumlamm Date: Fri, 12 Feb 2021 13:30:35 +0100 Subject: [PATCH 3/3] Fix skipRandomNumbers example --- lib/pure/random.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 8334f6e44e631..f7f0f457d74ef 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -175,10 +175,10 @@ proc skipRandomNumbers*(s: var Rand) = const spawns = 4 const numbers = 100000 - proc randomSum(rand: Rand): int = - var r = rand + proc randomSum(r: Rand): int = + var r = r for i in 1..numbers: - result += rand(0..10) + result += r.rand(0..10) var r = initRand(2019) var vals: array[spawns, FlowVar[int]]