distributions.odin 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. package rand
  2. import "core:math"
  3. float64_uniform :: float64_range
  4. float32_uniform :: float32_range
  5. // Triangular Distribution
  6. // See: http://wikipedia.org/wiki/Triangular_distribution
  7. @(require_results)
  8. float64_triangular :: proc(lo, hi: f64, mode: Maybe(f64), r: ^Rand = nil) -> f64 {
  9. if hi-lo == 0 {
  10. return lo
  11. }
  12. lo, hi := lo, hi
  13. u := float64(r)
  14. c := f64(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
  15. if u > c {
  16. u = 1-u
  17. c = 1-c
  18. lo, hi = hi, lo
  19. }
  20. return lo + (hi - lo) * math.sqrt(u * c)
  21. }
  22. // Triangular Distribution
  23. // See: http://wikipedia.org/wiki/Triangular_distribution
  24. @(require_results)
  25. float32_triangular :: proc(lo, hi: f32, mode: Maybe(f32), r: ^Rand = nil) -> f32 {
  26. if hi-lo == 0 {
  27. return lo
  28. }
  29. lo, hi := lo, hi
  30. u := float32(r)
  31. c := f32(0.5) if mode == nil else clamp((mode.?-lo) / (hi-lo), 0, 1)
  32. if u > c {
  33. u = 1-u
  34. c = 1-c
  35. lo, hi = hi, lo
  36. }
  37. return lo + (hi - lo) * math.sqrt(u * c)
  38. }
  39. // Normal/Gaussian Distribution
  40. @(require_results)
  41. float64_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
  42. return norm_float64(r) * stddev + mean
  43. }
  44. // Normal/Gaussian Distribution
  45. @(require_results)
  46. float32_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
  47. return f32(float64_normal(f64(mean), f64(stddev), r))
  48. }
  49. // Log Normal Distribution
  50. @(require_results)
  51. float64_log_normal :: proc(mean, stddev: f64, r: ^Rand = nil) -> f64 {
  52. return math.exp(float64_normal(mean, stddev, r))
  53. }
  54. // Log Normal Distribution
  55. @(require_results)
  56. float32_log_normal :: proc(mean, stddev: f32, r: ^Rand = nil) -> f32 {
  57. return f32(float64_log_normal(f64(mean), f64(stddev), r))
  58. }
  59. // Exponential Distribution
  60. // `lambda` is 1.0/(desired mean). It should be non-zero.
  61. // Return values range from
  62. // 0 to positive infinity if lambda > 0
  63. // negative infinity to 0 if lambda <= 0
  64. @(require_results)
  65. float64_exponential :: proc(lambda: f64, r: ^Rand = nil) -> f64 {
  66. return - math.ln(1 - float64(r)) / lambda
  67. }
  68. // Exponential Distribution
  69. // `lambda` is 1.0/(desired mean). It should be non-zero.
  70. // Return values range from
  71. // 0 to positive infinity if lambda > 0
  72. // negative infinity to 0 if lambda <= 0
  73. @(require_results)
  74. float32_exponential :: proc(lambda: f32, r: ^Rand = nil) -> f32 {
  75. return f32(float64_exponential(f64(lambda), r))
  76. }
  77. // Gamma Distribution (NOT THE GAMMA FUNCTION)
  78. //
  79. // Required: alpha > 0 and beta > 0
  80. //
  81. // math.pow(x, alpha-1) * math.exp(-x / beta)
  82. // pdf(x) = --------------------------------------------
  83. // math.gamma(alpha) * math.pow(beta, alpha)
  84. //
  85. // mean is alpha*beta, variance is math.pow(alpha*beta, 2)
  86. @(require_results)
  87. float64_gamma :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
  88. if alpha <= 0 || beta <= 0 {
  89. panic(#procedure + ": alpha and beta must be > 0.0")
  90. }
  91. LOG4 :: 1.3862943611198906188344642429163531361510002687205105082413600189
  92. SG_MAGIC_CONST :: 2.5040773967762740733732583523868748412194809812852436493487
  93. switch {
  94. case alpha > 1:
  95. // R.C.H. Cheng, "The generation of Gamma variables with non-integral shape parameters", Applied Statistics, (1977), 26, No. 1, p71-74
  96. ainv := math.sqrt(2 * alpha - 1)
  97. bbb := alpha - LOG4
  98. ccc := alpha + ainv
  99. for {
  100. u1 := float64(r)
  101. if !(1e-7 < u1 && u1 < 0.9999999) {
  102. continue
  103. }
  104. u2 := 1 - float64(r)
  105. v := math.ln(u1 / (1 - u1)) / ainv
  106. x := alpha * math.exp(v)
  107. z := u1 * u1 * u2
  108. t := bbb + ccc*v - x
  109. if t + SG_MAGIC_CONST - 4.5 * z >= 0 || t >= math.ln(z) {
  110. return x * beta
  111. }
  112. }
  113. case alpha == 1:
  114. // float64_exponential(1/beta)
  115. return -math.ln(1 - float64(r)) * beta
  116. case:
  117. // ALGORITHM GS of Statistical Computing - Kennedy & Gentle
  118. x: f64
  119. for {
  120. u := float64(r)
  121. b := (math.e + alpha) / math.e
  122. p := b * u
  123. if p <= 1 {
  124. x = math.pow(p, 1/alpha)
  125. } else {
  126. x = -math.ln((b - p) / alpha)
  127. }
  128. u1 := float64(r)
  129. if p > 1 {
  130. if u1 <= math.pow(x, alpha-1) {
  131. break
  132. }
  133. } else if u1 <= math.exp(-x) {
  134. break
  135. }
  136. }
  137. return x * beta
  138. }
  139. }
  140. // Gamma Distribution (NOT THE GAMMA FUNCTION)
  141. //
  142. // Required: alpha > 0 and beta > 0
  143. //
  144. // math.pow(x, alpha-1) * math.exp(-x / beta)
  145. // pdf(x) = --------------------------------------------
  146. // math.gamma(alpha) * math.pow(beta, alpha)
  147. //
  148. // mean is alpha*beta, variance is math.pow(alpha*beta, 2)
  149. @(require_results)
  150. float32_gamma :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
  151. return f32(float64_gamma(f64(alpha), f64(beta), r))
  152. }
  153. // Beta Distribution
  154. //
  155. // Required: alpha > 0 and beta > 0
  156. //
  157. // Return values range between 0 and 1
  158. @(require_results)
  159. float64_beta :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
  160. if alpha <= 0 || beta <= 0 {
  161. panic(#procedure + ": alpha and beta must be > 0.0")
  162. }
  163. // Knuth Vol 2 Ed 3 pg 134 "the beta distribution"
  164. y := float64_gamma(alpha, 1.0, r)
  165. if y != 0 {
  166. return y / (y + float64_gamma(beta, 1.0, r))
  167. }
  168. return 0
  169. }
  170. // Beta Distribution
  171. //
  172. // Required: alpha > 0 and beta > 0
  173. //
  174. // Return values range between 0 and 1
  175. @(require_results)
  176. float32_beta :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
  177. return f32(float64_beta(f64(alpha), f64(beta), r))
  178. }
  179. // Pareto distribution, `alpha` is the shape parameter.
  180. // https://wikipedia.org/wiki/Pareto_distribution
  181. @(require_results)
  182. float64_pareto :: proc(alpha: f64, r: ^Rand = nil) -> f64 {
  183. return math.pow(1 - float64(r), -1.0 / alpha)
  184. }
  185. // Pareto distribution, `alpha` is the shape parameter.
  186. // https://wikipedia.org/wiki/Pareto_distribution
  187. @(require_results)
  188. float32_pareto :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
  189. return f32(float64_pareto(f64(alpha), r))
  190. }
  191. // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
  192. @(require_results)
  193. float64_weibull :: proc(alpha, beta: f64, r: ^Rand = nil) -> f64 {
  194. u := 1 - float64(r)
  195. return alpha * math.pow(-math.ln(u), 1.0/beta)
  196. }
  197. // Weibull distribution, `alpha` is the scale parameter, `beta` is the shape parameter.
  198. @(require_results)
  199. float32_weibull :: proc(alpha, beta: f32, r: ^Rand = nil) -> f32 {
  200. return f32(float64_weibull(f64(alpha), f64(beta), r))
  201. }
  202. // Circular Data (von Mises) Distribution
  203. // `mean_angle` is the in mean angle between 0 and 2pi radians
  204. // `kappa` is the concentration parameter which must be >= 0
  205. // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
  206. @(require_results)
  207. float64_von_mises :: proc(mean_angle, kappa: f64, r: ^Rand = nil) -> f64 {
  208. // Fisher, N.I., "Statistical Analysis of Circular Data", Cambridge University Press, 1993.
  209. mu := mean_angle
  210. if kappa <= 1e-6 {
  211. return math.TAU * float64(r)
  212. }
  213. s := 0.5 / kappa
  214. t := s + math.sqrt(1 + s*s)
  215. z: f64
  216. for {
  217. u1 := float64(r)
  218. z = math.cos(math.TAU * 0.5 * u1)
  219. d := z / (t + z)
  220. u2 := float64(r)
  221. if u2 < 1 - d*d || u2 <= (1-d)*math.exp(d) {
  222. break
  223. }
  224. }
  225. q := 1.0 / t
  226. f := (q + z) / (1 + q*z)
  227. u3 := float64(r)
  228. if u3 > 0.5 {
  229. return math.mod(mu + math.acos(f), math.TAU)
  230. } else {
  231. return math.mod(mu - math.acos(f), math.TAU)
  232. }
  233. }
  234. // Circular Data (von Mises) Distribution
  235. // `mean_angle` is the in mean angle between 0 and 2pi radians
  236. // `kappa` is the concentration parameter which must be >= 0
  237. // When `kappa` is zero, the Distribution is a uniform Distribution over the range 0 to 2pi
  238. @(require_results)
  239. float32_von_mises :: proc(mean_angle, kappa: f32, r: ^Rand = nil) -> f32 {
  240. return f32(float64_von_mises(f64(mean_angle), f64(kappa), r))
  241. }
  242. // Cauchy-Lorentz Distribution
  243. // `x_0` is the location, `gamma` is the scale where `gamma` > 0
  244. @(require_results)
  245. float64_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
  246. assert(gamma > 0)
  247. // Calculated from the inverse CDF
  248. return math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0
  249. }
  250. // Cauchy-Lorentz Distribution
  251. // `x_0` is the location, `gamma` is the scale where `gamma` > 0
  252. @(require_results)
  253. float32_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
  254. return f32(float64_cauchy_lorentz(f64(x_0), f64(gamma), r))
  255. }
  256. // Log Cauchy-Lorentz Distribution
  257. // `x_0` is the location, `gamma` is the scale where `gamma` > 0
  258. @(require_results)
  259. float64_log_cauchy_lorentz :: proc(x_0, gamma: f64, r: ^Rand = nil) -> f64 {
  260. assert(gamma > 0)
  261. return math.exp(math.tan(math.PI * (float64(r) - 0.5))*gamma + x_0)
  262. }
  263. // Log Cauchy-Lorentz Distribution
  264. // `x_0` is the location, `gamma` is the scale where `gamma` > 0
  265. @(require_results)
  266. float32_log_cauchy_lorentz :: proc(x_0, gamma: f32, r: ^Rand = nil) -> f32 {
  267. return f32(float64_log_cauchy_lorentz(f64(x_0), f64(gamma), r))
  268. }
  269. // Laplace Distribution
  270. // `b` is the scale where `b` > 0
  271. @(require_results)
  272. float64_laplace :: proc(mean, b: f64, r: ^Rand = nil) -> f64 {
  273. assert(b > 0)
  274. p := float64(r)-0.5
  275. return -math.sign(p)*math.ln(1 - 2*abs(p))*b + mean
  276. }
  277. // Laplace Distribution
  278. // `b` is the scale where `b` > 0
  279. @(require_results)
  280. float32_laplace :: proc(mean, b: f32, r: ^Rand = nil) -> f32 {
  281. return f32(float64_laplace(f64(mean), f64(b), r))
  282. }
  283. // Gompertz Distribution
  284. // `eta` is the shape, `b` is the scale
  285. // Both `eta` and `b` must be > 0
  286. @(require_results)
  287. float64_gompertz :: proc(eta, b: f64, r: ^Rand = nil) -> f64 {
  288. if eta <= 0 || b <= 0 {
  289. panic(#procedure + ": eta and b must be > 0.0")
  290. }
  291. p := float64(r)
  292. return math.ln(1 - math.ln(1 - p)/eta)/b
  293. }
  294. // Gompertz Distribution
  295. // `eta` is the shape, `b` is the scale
  296. // Both `eta` and `b` must be > 0
  297. @(require_results)
  298. float32_gompertz :: proc(eta, b: f32, r: ^Rand = nil) -> f32 {
  299. return f32(float64_gompertz(f64(eta), f64(b), r))
  300. }