1 ! Copyright (C) 2008 Doug Coleman.
2 ! See https://factorcode.org/license.txt for BSD license.
3 USING: accessors alien.data arrays assocs byte-arrays
4 combinators combinators.short-circuit hash-sets hashtables
5 hashtables.private kernel math math.bitwise math.constants
6 math.functions math.order namespaces sequences sequences.private
7 sets summary system vocabs ;
8 QUALIFIED-WITH: alien.c-types c
13 SYMBOL: system-random-generator
14 SYMBOL: secure-random-generator
15 SYMBOL: random-generator
17 GENERIC#: seed-random 1 ( rnd seed -- rnd )
18 GENERIC: random-32* ( rnd -- n )
19 GENERIC: random-bytes* ( n rnd -- byte-array )
21 M: object random-bytes*
22 [ integer>fixnum-strict [ (byte-array) ] keep ] dip
25 [ random-32* 2over c:int c:set-alien-value ] keep
26 ] while over zero? [ 2drop ] [
27 random-32* c:int <ref> swap head 0 pick copy-unsafe
31 4 swap random-bytes* c:uint deref ;
33 ERROR: no-random-number-generator ;
35 M: no-random-number-generator summary
36 drop "Random number generator is not defined." ;
38 M: f random-bytes* no-random-number-generator ;
40 M: f random-32* no-random-number-generator ;
43 random-generator get random-32* ;
45 : random-bytes ( n -- byte-array )
46 random-generator get random-bytes* ;
50 :: (random-bits) ( numbits rnd -- n )
52 rnd random-32* numbits 32 - [ dup 32 > ] [
53 [ 32 shift rnd random-32* + ] [ 32 - ] bi*
55 [ shift ] keep rnd random-32* swap bits +
58 rnd random-32* numbits bits
63 : random-bits ( numbits -- n )
64 random-generator get (random-bits) ;
66 : random-bits* ( numbits -- n )
67 1 - [ random-bits ] keep set-bit ;
69 GENERIC#: random* 1 ( obj rnd -- elt )
71 : random ( obj -- elt )
72 random-generator get random* ;
74 : randoms ( length obj -- seq )
75 random-generator get '[ _ _ random* ] replicate ;
79 : next-power-of-2-bits ( m -- numbits )
80 dup 2 <= [ drop 1 ] [ 1 - log2 1 + ] if ; inline
82 :: random-integer ( m rnd -- n )
84 rnd random-32* { integer } declare 32 m next-power-of-2-bits 32 - [ dup 0 > ] [
85 [ 32 shift rnd random-32* { integer } declare + ] [ 32 + ] [ 32 - ] tri*
86 ] while drop [ m * ] [ neg shift ] bi*
91 M: fixnum random* random-integer ;
93 M: bignum random* random-integer ;
96 [ f ] swap '[ [ length _ random* ] keep nth ] if-empty ;
98 M: assoc random* [ >alist ] dip random* ;
101 [ dup assoc-size [ drop f ] ] dip '[
102 [ 0 ] [ array>> ] [ _ random* ] tri* 1 + [
103 [ 2dup array-nth tombstone? [ 2 + ] 2dip ] loop
105 [ array-nth ] [ [ 1 + ] dip array-nth ] 2bi 2array
108 M: sets:set random* [ members ] dip random* ;
111 [ dup cardinality [ drop f ] ] dip '[
112 [ 0 ] [ array>> ] [ _ random* ] tri* 1 + [
113 [ 2dup array-nth tombstone? [ 1 + ] 2dip ] loop
114 ] times [ 1 - ] dip array-nth
117 : randomize-n-last ( seq n -- seq )
118 [ dup length dup ] dip - 1 max '[ dup _ > ]
119 random-generator get '[
120 [ _ random* ] [ 1 - ] bi
121 [ pick exchange-unsafe ] keep
124 : randomize ( seq -- randomized )
125 dup length randomize-n-last ;
127 ERROR: too-many-samples seq n ;
129 : sample ( seq n -- seq' )
130 2dup [ length ] dip < [ too-many-samples ] when
131 [ [ length <iota> >array ] dip [ randomize-n-last ] keep tail-slice* ]
132 [ drop ] 2bi nths-unsafe ;
134 : delete-random ( seq -- elt )
135 [ length random ] keep [ nth ] 2keep remove-nth! drop ;
137 : with-random ( rnd quot -- )
138 random-generator swap with-variable ; inline
140 : with-system-random ( quot -- )
141 system-random-generator get swap with-random ; inline
143 : with-secure-random ( quot -- )
144 secure-random-generator get swap with-random ; inline
148 : (uniform-random-float) ( min max rnd -- n )
149 [ random-32* ] keep random-32* [ >float ] bi@
151 [ over - 2.0 -64 ^ * ] dip
156 : uniform-random-float ( min max -- n )
157 random-generator get (uniform-random-float) ; inline
160 [ f ] swap '[ 0.0 _ (uniform-random-float) ] if-zero ; inline
164 ! XXX: rename to random-unit*
165 : (random-unit) ( rnd -- n )
166 [ 0.0 1.0 ] dip (uniform-random-float) ; inline
170 : random-unit ( -- n )
171 random-generator get (random-unit) ; inline
173 : random-units ( length -- sequence )
174 random-generator get '[ _ (random-unit) ] replicate ;
178 : (cos-random-float) ( -- n )
179 0. 2pi uniform-random-float cos ;
181 : (log-sqrt-random-float) ( -- n )
182 random-unit log -2. * sqrt ;
186 : normal-random-float ( mean sigma -- n )
187 (cos-random-float) (log-sqrt-random-float) * * + ;
189 : lognormal-random-float ( mean sigma -- n )
190 normal-random-float e^ ;
192 : exponential-random-float ( lambda -- n )
193 random-unit log neg swap / ;
195 : weibull-random-float ( alpha beta -- n )
197 [ random-unit log neg ] dip
201 : pareto-random-float ( k alpha -- n )
202 [ random-unit ] dip recip ^ /f ;
206 :: (gamma-random-float>1) ( alpha beta -- n )
207 ! Uses R.C.H. Cheng, "The generation of Gamma
208 ! variables with non-integral shape parameters",
209 ! Applied Statistics, (1977), 26, No. 1, p71-74
210 random-generator get :> rnd
211 2. alpha * 1 - sqrt :> ainv
212 alpha 4. log - :> bbb
215 0 :> r! 0 :> z! 0 :> result! ! initialize locals
218 [ 1. 4.5 log + + z 4.5 * - 0 >= ]
222 rnd (random-unit) :> u1
223 rnd (random-unit) :> u2
225 u1 1. u1 - / log ainv / :> v
233 : (gamma-random-float=1) ( alpha beta -- n )
234 nip random-unit log neg * ;
236 :: (gamma-random-float<1) ( alpha beta -- n )
237 ! Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle
238 random-generator get :> rnd
241 0 :> x! 0 :> p! ! initialize locals
244 rnd (random-unit) x alpha 1 - ^ >
246 rnd (random-unit) x neg e^ >
249 rnd (random-unit) b * p!
253 b p - alpha / log neg
255 ] do while x beta * ;
259 : gamma-random-float ( alpha beta -- n )
261 { [ over 1 > ] [ (gamma-random-float>1) ] }
262 { [ over 1 = ] [ (gamma-random-float=1) ] }
263 [ (gamma-random-float<1) ]
266 : beta-random-float ( alpha beta -- n )
267 [ 1. gamma-random-float ] dip over zero?
268 [ 2drop 0 ] [ 1. gamma-random-float dupd + / ] if ;
270 :: von-mises-random-float ( mu kappa -- n )
271 ! Based upon an algorithm published in: Fisher, N.I.,
272 ! "Statistical Analysis of Circular Data", Cambridge
273 ! University Press, 1993.
274 random-generator get :> rnd
276 2pi rnd (random-unit) *
278 4. kappa sq * 1. + sqrt 1. + :> a
279 a 2. a * sqrt - 2. kappa * / :> b
280 b sq 1. + 2. b * / :> r
282 0 :> c! 0 :> _f! ! initialize locals
285 [ 2. c - c * < ] [ 1. c - e^ c * <= ]
288 rnd (random-unit) pi * cos :> z
289 r z * 1. + r z + / _f!
294 rnd (random-unit) 0.5 > [ + ] [ - ] if
299 :: (triangular-random-float) ( low high mode -- n )
300 mode low - high low - / :> c!
303 u c > [ 1. u - u! 1. c - c! swap ] when
304 [ - u c * sqrt * ] keep + ;
308 : triangular-random-float ( low high -- n )
309 2dup + 2 /f (triangular-random-float) ;
311 : laplace-random-float ( mean scale -- n )
312 random-unit dup 0.5 <
313 [ 2 * log ] [ 1 swap - 2 * log neg ] if * + ;
315 : cauchy-random-float ( median scale -- n )
316 random-unit 0.5 - pi * tan * + ;
318 : chi-square-random-float ( dof -- n )
319 [ 0.5 ] dip 2 * gamma-random-float ;
321 : student-t-random-float ( dof -- n )
322 [ 0 1 normal-random-float ] dip
323 [ chi-square-random-float ] [ / ] bi sqrt / ;
325 : inv-gamma-random-float ( shape scale -- n )
326 recip gamma-random-float recip ;
328 : rayleigh-random-float ( mode -- n )
329 random-unit log -2 * sqrt * ;
331 : gumbel-random-float ( loc scale -- n )
332 random-unit log neg log * - ;
334 : logistic-random-float ( loc scale -- n )
335 random-unit dup 1 swap - / log * + ;
337 : power-random-float ( alpha -- n )
338 [ random-unit log e^ 1 swap - ] dip recip ^ ;
341 : poisson-random-float ( mean -- n )
342 [ -1 0 ] dip [ 2dup < ] random-generator get '[
343 [ 1 + ] 2dip [ _ (random-unit) log neg + ] dip
346 :: binomial-random ( n p -- x )
347 n assert-non-negative drop
350 { [ p 0.0 = ] [ 0 ] }
351 { [ p 1.0 = ] [ n ] }
352 { [ n 1 = ] [ random-unit p < 1 0 ? ] }
353 { [ p 0.5 > ] [ n dup 1.0 p - binomial-random - ] }
355 ! BG: Geometric method by Devroye with running time of O(n*p).
356 ! https://dl.acm.org/doi/pdf/10.1145/42372.42381
358 0 0 random-generator get '[
359 _ (random-unit) log c /i 1 + +
360 dup n <= dup [ [ 1 + ] 2dip ] when
364 ! BTRS: Transformed rejection with squeeze method by Wolfgang Hörmann
365 ! https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.47.8407&rep=rep1&type=pdf
366 n p * 10.0 >= p 0.5 <= and t assert=
367 random-generator get :> rnd
369 n p * 1.0 p - * sqrt :> spq
370 1.15 2.53 spq * + :> b
371 -0.0873 0.0248 b * + 0.01 p * + :> a
375 2.83 5.1 b / + spq * :> alpha
376 p 1.0 p - / log :> lpq
378 m 1 + lgamma n m - 1 + lgamma + :> h
381 rnd (random-unit) 0.5 - :> u
382 rnd (random-unit) :> v
384 drop 2.0 a us / * b + u * c + floor >integer dup :> k
386 { [ us 0.07 >= ] [ v vr <= ] } 0&& [ f ] [
387 alpha a us sq / b + / v * log
388 h k 1 + lgamma - n k - 1 +
389 lgamma - k m - lpq * + >
396 "p must be in the range 0.0 <= p <= 1.0" throw
400 { [ os windows? ] [ "random.windows" require ] }
401 { [ os unix? ] [ "random.unix" require ] }
404 "random.mersenne-twister" require