random.nim 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2017 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## Nim's standard random number generator. Based on
  10. ## the ``xoroshiro128+`` (xor/rotate/shift/rotate) library.
  11. ## * More information: http://xoroshiro.di.unimi.it/
  12. ## * C implementation: http://xoroshiro.di.unimi.it/xoroshiro128plus.c
  13. ##
  14. ## **Do not use this module for cryptographic purposes!**
  15. import algorithm #For upperBound
  16. include "system/inclrtl"
  17. {.push debugger:off.}
  18. when defined(JS):
  19. type ui = uint32
  20. const randMax = 4_294_967_295u32
  21. else:
  22. type ui = uint64
  23. const randMax = 18_446_744_073_709_551_615u64
  24. type
  25. Rand* = object ## State of the random number generator.
  26. ## The procs that use the default state
  27. ## are **not** thread-safe!
  28. a0, a1: ui
  29. when defined(JS):
  30. var state = Rand(
  31. a0: 0x69B4C98Cu32,
  32. a1: 0xFED1DD30u32) # global for backwards compatibility
  33. else:
  34. # racy for multi-threading but good enough for now:
  35. var state = Rand(
  36. a0: 0x69B4C98CB8530805u64,
  37. a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility
  38. proc rotl(x, k: ui): ui =
  39. result = (x shl k) or (x shr (ui(64) - k))
  40. proc next*(r: var Rand): uint64 =
  41. ## Uses the state to compute a new ``uint64`` random number.
  42. let s0 = r.a0
  43. var s1 = r.a1
  44. result = s0 + s1
  45. s1 = s1 xor s0
  46. r.a0 = rotl(s0, 55) xor s1 xor (s1 shl 14) # a, b
  47. r.a1 = rotl(s1, 36) # c
  48. proc skipRandomNumbers*(s: var Rand) =
  49. ## This is the jump function for the generator. It is equivalent
  50. ## to 2^64 calls to next(); it can be used to generate 2^64
  51. ## non-overlapping subsequences for parallel computations.
  52. when defined(JS):
  53. const helper = [0xbeac0467u32, 0xd86b048bu32]
  54. else:
  55. const helper = [0xbeac0467eba5facbu64, 0xd86b048b86aa9922u64]
  56. var
  57. s0 = ui 0
  58. s1 = ui 0
  59. for i in 0..high(helper):
  60. for b in 0 ..< 64:
  61. if (helper[i] and (ui(1) shl ui(b))) != 0:
  62. s0 = s0 xor s.a0
  63. s1 = s1 xor s.a1
  64. discard next(s)
  65. s.a0 = s0
  66. s.a1 = s1
  67. proc random*(max: int): int {.benign, deprecated.} =
  68. ## Returns a random number in the range 0..max-1. The sequence of
  69. ## random number is always the same, unless `randomize` is called
  70. ## which initializes the random number generator with a "random"
  71. ## number, i.e. a tickcount. **Deprecated since version 0.18.0**.
  72. ## Use ``rand`` instead.
  73. while true:
  74. let x = next(state)
  75. if x < randMax - (randMax mod ui(max)):
  76. return int(x mod uint64(max))
  77. proc random*(max: float): float {.benign, deprecated.} =
  78. ## Returns a random number in the range 0..<max. The sequence of
  79. ## random number is always the same, unless `randomize` is called
  80. ## which initializes the random number generator with a "random"
  81. ## number, i.e. a tickcount. **Deprecated since version 0.18.0**.
  82. ## Use ``rand`` instead.
  83. let x = next(state)
  84. when defined(JS):
  85. result = (float(x) / float(high(uint32))) * max
  86. else:
  87. let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
  88. result = (cast[float](u) - 1.0) * max
  89. proc random*[T](x: HSlice[T, T]): T {.deprecated.} =
  90. ## For a slice `a .. b` returns a value in the range `a .. b-1`.
  91. ## **Deprecated since version 0.18.0**.
  92. ## Use ``rand`` instead.
  93. result = T(random(x.b - x.a)) + x.a
  94. proc random*[T](a: openArray[T]): T {.deprecated.} =
  95. ## returns a random element from the openarray `a`.
  96. ## **Deprecated since version 0.18.0**.
  97. ## Use ``rand`` instead.
  98. result = a[random(a.low..a.len)]
  99. proc rand*(r: var Rand; max: Natural): int {.benign.} =
  100. ## Returns a random number in the range 0..max. The sequence of
  101. ## random number is always the same, unless `randomize` is called
  102. ## which initializes the random number generator with a "random"
  103. ## number, i.e. a tickcount.
  104. if max == 0: return
  105. while true:
  106. let x = next(r)
  107. if x <= randMax - (randMax mod ui(max)):
  108. return int(x mod (uint64(max)+1u64))
  109. proc rand*(max: int): int {.benign.} =
  110. ## Returns a random number in the range 0..max. The sequence of
  111. ## random number is always the same, unless `randomize` is called
  112. ## which initializes the random number generator with a "random"
  113. ## number, i.e. a tickcount.
  114. rand(state, max)
  115. proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
  116. ## Returns a random number in the range 0..max. The sequence of
  117. ## random number is always the same, unless `randomize` is called
  118. ## which initializes the random number generator with a "random"
  119. ## number, i.e. a tickcount.
  120. let x = next(r)
  121. when defined(JS):
  122. result = (float(x) / float(high(uint32))) * max
  123. else:
  124. let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
  125. result = (cast[float](u) - 1.0) * max
  126. proc rand*(max: float): float {.benign.} =
  127. ## Returns a random number in the range 0..max. The sequence of
  128. ## random number is always the same, unless `randomize` is called
  129. ## which initializes the random number generator with a "random"
  130. ## number, i.e. a tickcount.
  131. rand(state, max)
  132. proc rand*[T](r: var Rand; x: HSlice[T, T]): T =
  133. ## For a slice `a .. b` returns a value in the range `a .. b`.
  134. result = T(rand(r, x.b - x.a)) + x.a
  135. proc rand*[T](x: HSlice[T, T]): T =
  136. ## For a slice `a .. b` returns a value in the range `a .. b`.
  137. result = rand(state, x)
  138. proc rand*[T](r: var Rand; a: openArray[T]): T {.deprecated.} =
  139. ## returns a random element from the openarray `a`.
  140. ## **Deprecated since v0.20.0:** use ``sample`` instead.
  141. result = a[rand(r, a.low..a.high)]
  142. proc rand*[T](a: openArray[T]): T {.deprecated.} =
  143. ## returns a random element from the openarray `a`.
  144. ## **Deprecated since v0.20.0:** use ``sample`` instead.
  145. result = a[rand(a.low..a.high)]
  146. proc sample*[T](r: var Rand; a: openArray[T]): T =
  147. ## returns a random element from openArray ``a`` using state in ``r``.
  148. result = a[r.rand(a.low..a.high)]
  149. proc sample*[T](a: openArray[T]): T =
  150. ## returns a random element from openArray ``a`` using non-thread-safe state.
  151. result = a[rand(a.low..a.high)]
  152. proc sample*[T, U](r: var Rand; a: openArray[T], cdf: openArray[U]): T =
  153. ## Sample one element from openArray ``a`` when it has cumulative distribution
  154. ## function (CDF) ``cdf`` (not necessarily normalized, any type of elements
  155. ## convertible to ``float``). Uses state in ``r``. E.g.:
  156. ##
  157. ## .. code-block:: nim
  158. ## let val = [ "a", "b", "c", "d" ] # some values
  159. ## var cnt = [1, 2, 3, 4] # histogram of counts
  160. ## echo r.sample(val, cnt.cumsummed) # echo a sample
  161. assert(cdf.len == a.len) # Two basic sanity checks.
  162. assert(float(cdf[^1]) > 0.0)
  163. #While we could check cdf[i-1] <= cdf[i] for i in 1..cdf.len, that could get
  164. #awfully expensive even in debugging modes.
  165. let u = r.rand(float(cdf[^1]))
  166. a[cdf.upperBound(U(u))]
  167. proc sample*[T, U](a: openArray[T], cdf: openArray[U]): T =
  168. ## Like ``sample(var Rand; openArray[T], openArray[U])``, but uses default
  169. ## non-thread-safe state.
  170. state.sample(a, cdf)
  171. proc initRand*(seed: int64): Rand =
  172. ## Creates a new ``Rand`` state from ``seed``.
  173. result.a0 = ui(seed shr 16)
  174. result.a1 = ui(seed and 0xffff)
  175. discard next(result)
  176. proc randomize*(seed: int64) {.benign.} =
  177. ## Initializes the default random number generator
  178. ## with a specific seed.
  179. state = initRand(seed)
  180. proc shuffle*[T](r: var Rand; x: var openArray[T]) =
  181. ## Swaps the positions of elements in a sequence randomly.
  182. for i in countdown(x.high, 1):
  183. let j = r.rand(i)
  184. swap(x[i], x[j])
  185. proc shuffle*[T](x: var openArray[T]) =
  186. ## Swaps the positions of elements in a sequence randomly.
  187. shuffle(state, x)
  188. when not defined(nimscript):
  189. import times
  190. proc randomize*() {.benign.} =
  191. ## Initializes the random number generator with a "random"
  192. ## number, i.e. a tickcount. Note: Does not work for NimScript.
  193. when defined(js):
  194. let time = int64(times.epochTime() * 1_000_000_000)
  195. randomize(time)
  196. else:
  197. let now = times.getTime()
  198. randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond)
  199. {.pop.}
  200. when isMainModule:
  201. proc main =
  202. var occur: array[1000, int]
  203. var x = 8234
  204. for i in 0..100_000:
  205. x = rand(high(occur))
  206. inc occur[x]
  207. for i, oc in occur:
  208. if oc < 69:
  209. doAssert false, "too few occurrences of " & $i
  210. elif oc > 150:
  211. doAssert false, "too many occurrences of " & $i
  212. var a = [0, 1]
  213. shuffle(a)
  214. doAssert a[0] == 1
  215. doAssert a[1] == 0
  216. doAssert rand(0) == 0
  217. doAssert rand("a") == 'a'
  218. when compileOption("rangeChecks"):
  219. try:
  220. discard rand(-1)
  221. doAssert false
  222. except RangeError:
  223. discard
  224. try:
  225. discard rand(-1.0)
  226. doAssert false
  227. except RangeError:
  228. discard
  229. # don't use causes integer overflow
  230. doAssert compiles(random[int](low(int) .. high(int)))
  231. main()