]> gitweb.factorcode.org Git - factor.git/blob - basis/random/random.factor
random: 40% faster random-bytes*.
[factor.git] / basis / random / random.factor
1 ! Copyright (C) 2008 Doug Coleman.
2 ! See http://factorcode.org/license.txt for BSD license.
3 USING: accessors alien.data arrays assocs byte-arrays
4 byte-vectors combinators combinators.short-circuit fry
5 hashtables hashtables.private hash-sets hints io.backend
6 io.binary kernel locals math math.bitwise math.constants
7 math.functions math.order math.ranges namespaces sequences
8 sequences.private sets summary system typed vocabs ;
9 QUALIFIED-WITH: alien.c-types c
10 QUALIFIED-WITH: sets sets
11 IN: random
12
13 SYMBOL: system-random-generator
14 SYMBOL: secure-random-generator
15 SYMBOL: random-generator
16
17 GENERIC# seed-random 1 ( tuple seed -- tuple' )
18 GENERIC: random-32* ( tuple -- r )
19 GENERIC: random-bytes* ( n tuple -- byte-array )
20
21 M: object random-bytes* ( n tuple -- byte-array )
22     [ integer>fixnum-strict [ <byte-array> ] keep ] dip
23     [ over 4 >= ] [
24         [ 4 - ] 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
28     ] if ;
29
30 M: object random-32* ( tuple -- r )
31     4 swap random-bytes* c:uint deref ;
32
33 ERROR: no-random-number-generator ;
34
35 M: no-random-number-generator summary
36     drop "Random number generator is not defined." ;
37
38 M: f random-bytes* ( n obj -- * ) no-random-number-generator ;
39
40 M: f random-32* ( obj -- * ) no-random-number-generator ;
41
42 : random-32 ( -- n )
43     random-generator get random-32* ;
44
45 : random-bytes ( n -- byte-array )
46     random-generator get random-bytes* ;
47
48 <PRIVATE
49
50 : #bits ( n -- bits )
51     dup 2 <= [ drop 1 ] [ 1 - log2 1 + ] if ; inline
52
53 :: (random-bits) ( n bits obj -- n' )
54     obj random-32* 32 bits 32 - [ dup 0 > ] [
55         [ 32 shift obj random-32* + ] [ 32 + ] [ 32 - ] tri*
56     ] while drop [ n * ] [ neg shift ] bi* ; inline
57
58 : ((random-integer)) ( n obj -- n' )
59     [ dup #bits ] dip (random-bits) ; inline
60
61 GENERIC# (random-integer) 1 ( n obj -- n )
62 M: fixnum (random-integer) ( n obj -- n' ) ((random-integer)) ;
63 M: bignum (random-integer) ( n obj -- n' ) ((random-integer)) ;
64
65 : random-integer ( n -- n' )
66     random-generator get (random-integer) ;
67
68 PRIVATE>
69
70 : random-bits ( numbits -- r )
71     [ 2^ ] keep random-generator get (random-bits) ;
72
73 : random-bits* ( numbits -- n )
74     1 - [ random-bits ] keep set-bit ;
75
76 GENERIC: random ( obj -- elt )
77
78 M: integer random [ f ] [ random-integer ] if-zero ;
79
80 M: sequence random
81     [ f ] [
82         [ length random-integer ] keep nth
83     ] if-empty ;
84
85 M: assoc random >alist random ;
86
87 M: hashtable random
88     dup assoc-size [ drop f ] [
89         [ 0 ] [ array>> ] [ random ] tri* 1 + [
90             [ 2dup array-nth tombstone? [ 2 + ] 2dip ] loop
91         ] times [ 2 - ] dip
92         [ array-nth ] [ [ 1 + ] dip array-nth ] 2bi 2array
93     ] if-zero ;
94
95 M: sets:set random members random ;
96
97 M: hash-set random
98     dup cardinality [ drop f ] [
99         [ 0 ] [ array>> ] [ random ] tri* 1 + [
100             [ 2dup array-nth tombstone? [ 1 + ] 2dip ] loop
101         ] times [ 1 - ] dip array-nth
102     ] if-zero ;
103
104 : randomize-n-last ( seq n -- seq )
105     [ dup length dup ] dip - 1 max '[ dup _ > ]
106     random-generator get '[
107         [ _ (random-integer) ] [ 1 - ] bi
108         [ pick exchange-unsafe ] keep
109     ] while drop ;
110
111 : randomize ( seq -- randomized )
112     dup length randomize-n-last ;
113
114 ERROR: too-many-samples seq n ;
115
116 : sample ( seq n -- seq' )
117     2dup [ length ] dip < [ too-many-samples ] when
118     [ [ length iota >array ] dip [ randomize-n-last ] keep tail-slice* ]
119     [ drop ] 2bi nths-unsafe ;
120
121 : delete-random ( seq -- elt )
122     [ length random-integer ] keep
123     [ nth ] 2keep remove-nth! drop ;
124
125 : with-random ( tuple quot -- )
126     random-generator swap with-variable ; inline
127
128 : with-system-random ( quot -- )
129     system-random-generator get swap with-random ; inline
130
131 : with-secure-random ( quot -- )
132     secure-random-generator get swap with-random ; inline
133
134 <PRIVATE
135
136 : (uniform-random-float) ( min max obj -- n )
137     [ random-32* ] keep random-32* [ >float ] bi@
138     2.0 32 ^ * +
139     [ over - 2.0 -64 ^ * ] dip
140     * + ; inline
141
142 PRIVATE>
143
144 : uniform-random-float ( min max -- n )
145     random-generator get (uniform-random-float) ; inline
146
147 M: float random [ f ] [ 0.0 swap uniform-random-float ] if-zero ;
148
149 <PRIVATE
150
151 : (random-unit) ( obj -- n )
152     [ 0.0 1.0 ] dip (uniform-random-float) ; inline
153
154 PRIVATE>
155
156 : random-unit ( -- n )
157     random-generator get (random-unit) ; inline
158
159 : random-units ( length -- sequence )
160     random-generator get '[ _ (random-unit) ] replicate ;
161
162 : random-integers ( length n -- sequence )
163     random-generator get '[ _ _ (random-integer) ] replicate ;
164
165 : (cos-random-float) ( -- n )
166     0. 2pi uniform-random-float cos ;
167
168 : (log-sqrt-random-float) ( -- n )
169     random-unit log -2. * sqrt ;
170
171 : normal-random-float ( mean sigma -- n )
172     (cos-random-float) (log-sqrt-random-float) * * + ;
173
174 : lognormal-random-float ( mean sigma -- n )
175     normal-random-float e^ ;
176
177 : exponential-random-float ( lambda -- n )
178     random-unit log neg swap / ;
179
180 : weibull-random-float ( alpha beta -- n )
181     [
182         [ random-unit log neg ] dip
183         1. swap / ^
184     ] dip * ;
185
186 : pareto-random-float ( k alpha -- n )
187     [ random-unit ] dip recip ^ /f ;
188
189 :: (gamma-random-float>1) ( alpha beta -- n )
190     ! Uses R.C.H. Cheng, "The generation of Gamma
191     ! variables with non-integral shape parameters",
192     ! Applied Statistics, (1977), 26, No. 1, p71-74
193     random-generator get :> rnd
194     2. alpha * 1 - sqrt  :> ainv
195     alpha 4. log -       :> bbb
196     alpha ainv +         :> ccc
197
198     0 :> r! 0 :> z! 0 :> result! ! initialize locals
199     [
200         r {
201             [ 1. 4.5 log + + z 4.5 * - 0 >= ]
202             [ z log >= ]
203         } 1|| not
204     ] [
205         rnd (random-unit) :> u1
206         rnd (random-unit) :> u2
207
208         u1 1. u1 - / log ainv / :> v
209         alpha v e^ *            :> x
210         u1 sq u2 *              z!
211         bbb ccc v * + x -       r!
212
213         x beta *                result!
214     ] do while result ;
215
216 : (gamma-random-float=1) ( alpha beta -- n )
217     nip random-unit log neg * ;
218
219 :: (gamma-random-float<1) ( alpha beta -- n )
220     ! Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle
221     random-generator get :> rnd
222     alpha e + e / :> b
223
224     0 :> x! 0 :> p! ! initialize locals
225     [
226         p 1.0 > [
227             rnd (random-unit) x alpha 1 - ^ >
228         ] [
229             rnd (random-unit) x neg e^ >
230         ] if
231     ] [
232         rnd (random-unit) b * p!
233         p 1.0 <= [
234             p 1. alpha / ^
235         ] [
236             b p - alpha / log neg
237         ] if x!
238     ] do while x beta * ;
239
240 : gamma-random-float ( alpha beta -- n )
241     {
242         { [ over 1 > ] [ (gamma-random-float>1) ] }
243         { [ over 1 = ] [ (gamma-random-float=1) ] }
244         [ (gamma-random-float<1) ]
245     } cond ;
246
247 : beta-random-float ( alpha beta -- n )
248     [ 1. gamma-random-float ] dip over zero?
249     [ 2drop 0 ] [ 1. gamma-random-float dupd + / ] if ;
250
251 :: von-mises-random-float ( mu kappa -- n )
252     ! Based upon an algorithm published in: Fisher, N.I.,
253     ! "Statistical Analysis of Circular Data", Cambridge
254     ! University Press, 1993.
255     random-generator get :> rnd
256     kappa 1e-6 <= [
257         2pi rnd (random-unit) *
258     ] [
259         4. kappa sq * 1. + sqrt 1. + :> a
260         a 2. a * sqrt - 2. kappa * / :> b
261         b sq 1. + 2. b * /           :> r
262
263         0 :> c! 0 :> _f! ! initialize locals
264         [
265             rnd (random-unit) {
266                 [ 2. c - c * < ] [ 1. c - e^ c * <= ]
267             } 1|| not
268         ] [
269             rnd (random-unit) pi * cos :> z
270             r z * 1. + r z + /   _f!
271             r _f - kappa *       c!
272         ] do while
273
274         mu 2pi mod _f cos
275         rnd (random-unit) 0.5 > [ + ] [ - ] if
276     ] if ;
277
278 :: (triangular-random-float) ( low high mode -- n )
279     mode low - high low - / :> c!
280     random-unit :> u!
281     high low
282     u c > [ 1. u - u! 1. c - c! swap ] when
283     [ - u c * sqrt * ] keep + ;
284
285 : triangular-random-float ( low high -- n )
286     2dup + 2 /f (triangular-random-float) ;
287
288 : laplace-random-float ( mean scale -- n )
289     random-unit dup 0.5 <
290     [ 2 * log ] [ 1 swap - 2 * log neg ] if * + ;
291
292 : cauchy-random-float ( median scale -- n )
293     random-unit 0.5 - pi * tan * + ;
294
295 : chi-square-random-float ( dof -- n )
296     [ 0.5 ] dip 2 * gamma-random-float ;
297
298 : student-t-random-float ( dof -- n )
299     [ 0 1 normal-random-float ] dip
300     [ chi-square-random-float ] [ / ] bi sqrt / ;
301
302 : inv-gamma-random-float ( shape scale -- n )
303     recip gamma-random-float recip ;
304
305 : rayleigh-random-float ( mode -- n )
306     random-unit log -2 * sqrt * ;
307
308 : gumbel-random-float ( loc scale -- n )
309     random-unit log neg log * - ;
310
311 : logistic-random-float ( loc scale -- n )
312     random-unit dup 1 swap - / log * + ;
313
314 : power-random-float ( alpha -- n )
315     [ random-unit log e^ 1 swap - ] dip recip ^ ;
316
317 ! Box-Muller
318 : poisson-random-float ( mean -- n )
319     [ -1 0 ] dip [ 2dup < ] random-generator get '[
320         [ 1 + ] 2dip [ _ (random-unit) log neg + ] dip
321     ] while 2drop ;
322
323 {
324     { [ os windows? ] [ "random.windows" require ] }
325     { [ os unix? ] [ "random.unix" require ] }
326 } cond
327
328 "random.mersenne-twister" require