
Added docs for math/distributions (about 75% finished) Started docs for math/array (very incomplete)
31 lines
1.2 KiB
Racket
31 lines
1.2 KiB
Racket
#lang typed/racket/base
|
|
|
|
(require "../../../flonum.rkt"
|
|
"../../../base.rkt"
|
|
"../../../vector.rkt")
|
|
|
|
(provide flnormal-sample)
|
|
|
|
(: box-muller-transform (Float Float -> Float))
|
|
(define (box-muller-transform x y)
|
|
(cond [(and (fl= x 0.0) (fl= y 0.0)) 0.0]
|
|
[else (fl* (flsqrt (fl* -2.0 (fllog x)))
|
|
(flsin (fl* (fl* 2.0 pi) y)))]))
|
|
|
|
(: flnormal-sample (Flonum Flonum Integer -> FlVector))
|
|
;; The Box-Muller method has an bad reputation, but undeservedly:
|
|
;; 1. There's nothing unstable about the floating-point implementation of the transform
|
|
;; 2. It has good tail behavior (i.e. it can return very unlikely numbers)
|
|
;; 3. With today's RNGs, there's no need to worry about generating two random numbers
|
|
;; 4. With today's FPUs, there's no need to worry about computing `log' and `sin' (sheesh)
|
|
;; Points in favor: it's simple and fast
|
|
(define (flnormal-sample c s n)
|
|
(cond [(n . < . 0) (raise-argument-error 'flnormal-sample "Natural" 2 c s n)]
|
|
[else
|
|
(build-flvector
|
|
n (λ (_)
|
|
(let loop ()
|
|
(define r (box-muller-transform (random) (random)))
|
|
(if (rational? r) (fl+ c (fl* s r)) (loop)))))]))
|
|
|