trandom.nim 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. discard """
  2. joinable: false # to avoid messing with global rand state
  3. matrix: "--mm:refc; --mm:orc; --backend:js --jsbigint64:off -d:nimStringHash2; --backend:js --jsbigint64:on"
  4. """
  5. import std/[assertions, formatfloat]
  6. import std/[random, math, stats, sets, tables]
  7. import std/private/jsutils
  8. when not defined(js):
  9. import std/os
  10. randomize(233)
  11. proc main() =
  12. var occur: array[1000, int] = default(array[1000, int])
  13. for i in 0..100_000:
  14. let x = rand(high(occur))
  15. inc occur[x]
  16. doAssert max(occur) <= 140 and min(occur) >= 60 # gives some slack
  17. var a = [0, 1]
  18. shuffle(a)
  19. doAssert a in [[0,1], [1,0]]
  20. doAssert rand(0) == 0
  21. when not defined(nimscript):
  22. doAssert sample("a") == 'a'
  23. when compileOption("rangeChecks") and not defined(nimscript):
  24. doAssertRaises(RangeDefect):
  25. discard rand(-1)
  26. doAssertRaises(RangeDefect):
  27. discard rand(-1.0)
  28. # don't use causes integer overflow
  29. doAssert compiles(rand[int](low(int) .. high(int)))
  30. main()
  31. block:
  32. when not defined(js):
  33. doAssert almostEqual(rand(12.5), 7.355175342026979)
  34. doAssert almostEqual(rand(2233.3322), 499.342386778917)
  35. type DiceRoll = range[0..6]
  36. when not defined(js):
  37. doAssert rand(DiceRoll).int == 3
  38. elif compileOption("jsbigint64"):
  39. doAssert rand(DiceRoll).int == 1
  40. else:
  41. doAssert rand(DiceRoll).int == 6
  42. var rs: RunningStat
  43. for j in 1..5:
  44. for i in 1 .. 100_000:
  45. rs.push(gauss())
  46. doAssert abs(rs.mean-0) < 0.08, $rs.mean
  47. doAssert abs(rs.standardDeviation()-1.0) < 0.1
  48. let bounds = [3.5, 5.0]
  49. for a in [rs.max, -rs.min]:
  50. doAssert a >= bounds[0] and a <= bounds[1]
  51. rs.clear()
  52. block:
  53. type DiceRoll = range[3..6]
  54. var flag = false
  55. for i in 0..<100:
  56. if rand(5.DiceRoll) < 3:
  57. flag = true
  58. doAssert flag # because of: rand(max: int): int
  59. block: # random int
  60. block: # there might be some randomness
  61. var set = initHashSet[int](128)
  62. for i in 1..1000:
  63. incl(set, rand(high(int)))
  64. doAssert len(set) == 1000
  65. block: # single number bounds work
  66. var rand: int
  67. for i in 1..1000:
  68. rand = rand(1000)
  69. doAssert rand <= 1000
  70. doAssert rand >= 0
  71. block: # slice bounds work
  72. var rand: int
  73. for i in 1..1000:
  74. rand = rand(100..1000)
  75. doAssert rand <= 1000
  76. doAssert rand >= 100
  77. block: # again gives new numbers
  78. var rand1 = rand(1000000)
  79. when not (defined(js) or defined(nimscript)):
  80. os.sleep(200)
  81. var rand2 = rand(1000000)
  82. doAssert rand1 != rand2
  83. block: # random float
  84. block: # there might be some randomness
  85. var set = initHashSet[float](128)
  86. for i in 1..100:
  87. incl(set, rand(1.0))
  88. doAssert len(set) == 100
  89. block: # single number bounds work
  90. var rand: float
  91. for i in 1..1000:
  92. rand = rand(1000.0)
  93. doAssert rand <= 1000.0
  94. doAssert rand >= 0.0
  95. block: # slice bounds work
  96. var rand: float
  97. for i in 1..1000:
  98. rand = rand(100.0..1000.0)
  99. doAssert rand <= 1000.0
  100. doAssert rand >= 100.0
  101. block: # again gives new numbers
  102. var rand1: float = rand(1000000.0)
  103. when not (defined(js) or defined(nimscript)):
  104. os.sleep(200)
  105. var rand2: float = rand(1000000.0)
  106. doAssert rand1 != rand2
  107. block: # random sample
  108. block: # "non-uniform array sample unnormalized int CDF
  109. let values = [10, 20, 30, 40, 50] # values
  110. let counts = [4, 3, 2, 1, 0] # weights aka unnormalized probabilities
  111. var histo = initCountTable[int]()
  112. let cdf = counts.cumsummed # unnormalized CDF
  113. for i in 0 ..< 5000:
  114. histo.inc(sample(values, cdf))
  115. doAssert histo.len == 4 # number of non-zero in `counts`
  116. # Any one bin is a binomial random var for n samples, each with prob p of
  117. # adding a count to k; E[k]=p*n, Var k=p*(1-p)*n, approximately Normal for
  118. # big n. So, P(abs(k - p*n)/sqrt(p*(1-p)*n))>3.0) =~ 0.0027, while
  119. # P(wholeTestFails) =~ 1 - P(binPasses)^4 =~ 1 - (1-0.0027)^4 =~ 0.01.
  120. for i, c in counts:
  121. if c == 0:
  122. doAssert values[i] notin histo
  123. continue
  124. let p = float(c) / float(cdf[^1])
  125. let n = 5000.0
  126. let expected = p * n
  127. let stdDev = sqrt(n * p * (1.0 - p))
  128. doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
  129. block: # non-uniform array sample normalized float CDF
  130. let values = [10, 20, 30, 40, 50] # values
  131. let counts = [0.4, 0.3, 0.2, 0.1, 0] # probabilities
  132. var histo = initCountTable[int]()
  133. let cdf = counts.cumsummed # normalized CDF
  134. for i in 0 ..< 5000:
  135. histo.inc(sample(values, cdf))
  136. doAssert histo.len == 4 # number of non-zero in ``counts``
  137. for i, c in counts:
  138. if c == 0:
  139. doAssert values[i] notin histo
  140. continue
  141. let p = float(c) / float(cdf[^1])
  142. let n = 5000.0
  143. let expected = p * n
  144. let stdDev = sqrt(n * p * (1.0 - p))
  145. # NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01.
  146. doAssert abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev
  147. block:
  148. # 0 is a valid seed
  149. var r = initRand(0)
  150. doAssert r.rand(1.0) != r.rand(1.0)
  151. r = initRand(10)
  152. doAssert r.rand(1.0) != r.rand(1.0)
  153. # changing the seed changes the sequence
  154. var r1 = initRand(123)
  155. var r2 = initRand(124)
  156. doAssert r1.rand(1.0) != r2.rand(1.0)
  157. block: # bug #17467
  158. let n = 1000
  159. for i in -n .. n:
  160. var r = initRand(i)
  161. let x = r.rand(1.0)
  162. doAssert x > 1e-4, $(x, i)
  163. # This used to fail for each i in 0..<26844, i.e. the 1st produced value
  164. # was predictable and < 1e-4, skewing distributions.
  165. block: # bug #16360, Natural overload
  166. var r = initRand()
  167. template test(a) =
  168. let a2 = a
  169. block:
  170. let a3 = r.rand(a2)
  171. doAssert a3 <= a2
  172. doAssert a3.type is a2.type
  173. block:
  174. let a3 = rand(a2)
  175. doAssert a3 <= a2
  176. doAssert a3.type is a2.type
  177. test int.high
  178. test int.high - 1
  179. test int.high - 2
  180. test 0
  181. block: # same as above but use slice overload
  182. var r = initRand()
  183. template test[T](a: T) =
  184. let a2: T = a
  185. block:
  186. let a3 = r.rand(T(0) .. a2)
  187. doAssert a3 <= a2
  188. doAssert a3.type is a2.type
  189. block:
  190. let a3 = rand(T(0) .. a2)
  191. doAssert a3 <= a2
  192. doAssert a3.type is a2.type
  193. test cast[uint](int.high)
  194. test cast[uint](int.high) + 1
  195. when hasWorkingInt64 and defined(js):
  196. # weirdly this has to run only in JS for the final int32.high test
  197. # to be the same between C/C++ and --jsbigint64:on
  198. test uint64.high
  199. test uint64.high - 1
  200. test uint.high - 2
  201. test uint.high - 1
  202. test uint.high
  203. test int.high
  204. test int.high - 1
  205. test int.high - 2
  206. test 0
  207. test 0'u
  208. test 0'u64
  209. block: # bug #16296
  210. var r = initRand()
  211. template test(x) =
  212. let a2 = x
  213. let a3 = r.rand(a2)
  214. doAssert a3 <= a2.b
  215. doAssert a3 >= a2.a
  216. doAssert a3.type is a2.a.type
  217. test(-2 .. int.high-1)
  218. test(int.low .. int.high)
  219. test(int.low+1 .. int.high)
  220. test(int.low .. int.high-1)
  221. test(int.low .. 0)
  222. test(int.low .. -1)
  223. test(int.low .. 1)
  224. test(int64.low .. 1'i64)
  225. test(10'u64 .. uint64.high)
  226. block: # bug #17670
  227. type UInt48 = range[0'u64..2'u64^48-1]
  228. let x = rand(UInt48)
  229. doAssert x is UInt48
  230. block: # bug #17898
  231. # Checks whether `initRand()` generates unique states.
  232. # size should be 2^64, but we don't have time and space.
  233. # Disable this test for js until js gets proper skipRandomNumbers.
  234. when not defined(js):
  235. const size = 1000
  236. var
  237. rands: array[size, Rand]
  238. randSet: HashSet[Rand]
  239. for i in 0..<size:
  240. rands[i] = initRand()
  241. randSet.incl rands[i]
  242. doAssert randSet.len == size
  243. # Checks random number sequences overlapping.
  244. const numRepeat = 100
  245. for i in 0..<size:
  246. for j in 0..<numRepeat:
  247. discard rands[i].next
  248. doAssert rands[i] notin randSet
  249. block: # bug #22360
  250. const size = 1000
  251. var fc = 0
  252. var tc = 0
  253. for _ in 1..size:
  254. let s = rand(bool)
  255. if s:
  256. inc tc
  257. else:
  258. inc fc
  259. when defined(js) and not compileOption("jsbigint64"):
  260. doAssert (tc, fc) == (515, 485), $(tc, fc)
  261. else:
  262. doAssert (tc, fc) == (510, 490), $(tc, fc)
  263. block:
  264. when defined(js) and not compileOption("jsbigint64"):
  265. doAssert rand(int32.high) == 335507522
  266. else:
  267. doAssert rand(int32.high) == 607539621