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