trandom.nim 7.2 KB

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