trandom.nim 7.8 KB

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