Skip to content

Commit 2d84e9b

Browse files
samthpavpanchekha
andauthored
Allow supplying a prng (#112)
* Allow supplying a prng * Optimize random-bits for "small" numbers of bits. --------- Co-authored-by: Pavel Panchekha <[email protected]>
1 parent 5644a7c commit 2d84e9b

File tree

3 files changed

+47
-16
lines changed

3 files changed

+47
-16
lines changed

math-doc/math/scribblings/math-base.scrbl

+9-3
Original file line numberDiff line numberDiff line change
@@ -82,23 +82,29 @@ Like @racket[(apply + xs)], but incurs rounding error only once when adding inex
8282

8383
@section{Random Number Generation}
8484

85-
@defproc[(random-natural [k Integer]) Natural]{
85+
@defproc[(random-natural [k Integer] [p Pseudo-Random-Generator (current-pseudo-random-generator)]) Natural]{
8686
Returns a random natural number less than @racket[k], which must be positive.
8787
Use @racket[(random-natural k)] instead of @racket[(random k)] when @racket[k]
8888
could be larger than @racket[4294967087].
89+
90+
Uses @racket[p] for the underlying generation of randomness.
8991
}
9092

91-
@defproc[(random-integer [a Integer] [b Integer]) Integer]{
93+
@defproc[(random-integer [a Integer] [b Integer] [p Pseudo-Random-Generator (current-pseudo-random-generator)]) Integer]{
9294
Returns a random integer @racket[n] such that @racket[(a . <= . n)] and @racket[(n . < . b)].
95+
96+
Uses @racket[p] for the underlying generation of randomness.
9397
}
9498

95-
@defproc[(random-bits [num Integer]) Natural]{
99+
@defproc[(random-bits [num Integer] [p Pseudo-Random-Generator (current-pseudo-random-generator)]) Natural]{
96100
Returns a random natural smaller than @racket[(expt 2 num)]; @racket[num] must be non-negative.
97101
For powers of two, this is faster than using @racket[random-natural], which
98102
is implemented in terms of @racket[random-bits], using biased rejection sampling.
99103

100104
As an example of use, the significands of the numbers returned by @racket[bfrandom]
101105
are chosen by @racket[(random-bits (bf-precision))].
106+
107+
Uses @racket[p] for the underlying generation of randomness.
102108
}
103109

104110

math-lib/math/private/base/base-random.rkt

+23-13
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,49 @@
55
(provide random-bits random-natural random-integer)
66

77
;; Random bits are taken in blocks of this size:
8-
(define block-bits 29)
9-
(define block-size (arithmetic-shift 1 block-bits))
8+
(define block-bits : Nonnegative-Fixnum 29)
9+
(define block-size : Natural (arithmetic-shift 1 block-bits))
1010

11-
(: random-bits (Integer -> Natural))
12-
(define (random-bits bits)
11+
(: random-bits (case-> (Integer -> Natural)
12+
(Integer Pseudo-Random-Generator -> Natural)))
13+
(define (random-bits bits [prng (current-pseudo-random-generator)])
1314
(cond [(bits . < . 0) (raise-argument-error 'random-bits "Non-Negative-Integer" bits)]
1415
[(bits . = . 0) 0]
16+
[(not (fixnum? bits)) (raise-argument-error 'random-bits "reasonably sized integer" bits)]
17+
[(bits . <= . block-bits) (random (ann (fxlshift 1 bits) Nonnegative-Fixnum) prng)]
18+
[(bits . <= . (fx* 2 block-bits))
19+
;; this case is not always in the fixnum range on 32-bit platforms
20+
(define rem-bits (fx- bits block-bits))
21+
(bitwise-ior (arithmetic-shift (random block-size prng) rem-bits)
22+
(random (ann (fxlshift 1 rem-bits) Nonnegative-Fixnum) prng))]
1523
[else
1624
(define max-blocks (assert (quotient bits block-bits) index?))
1725
(define rem-bits (remainder bits block-bits))
1826
(let: loop : Natural ([blocks : Nonnegative-Fixnum 0]
19-
[r : Natural (random (fxlshift 1 rem-bits))])
27+
[r : Natural (random (fxlshift 1 rem-bits) prng)])
2028
(cond [(blocks . fx< . max-blocks)
2129
(loop (fx+ blocks 1)
2230
(bitwise-ior (arithmetic-shift r block-bits)
23-
(random block-size)))]
31+
(random block-size prng)))]
2432
[else r]))]))
2533

2634
(define random-max 4294967087)
2735

28-
(: random-natural (Integer -> Natural))
36+
(: random-natural (case-> (Integer -> Natural)
37+
(Integer Pseudo-Random-Generator -> Natural)))
2938
;; Returns a random integer in the interval [0..n)
30-
(define (random-natural n)
39+
(define (random-natural n [prng (current-pseudo-random-generator)])
3140
(cond
3241
[(n . <= . 0) (raise-argument-error 'random-natural "Positive-Integer" n)]
33-
[(n . <= . random-max) (random n)]
42+
[(n . <= . random-max) (random n prng)]
3443
[else
3544
(define bits (integer-length (- n 1)))
3645
(let loop ()
37-
(define r (random-bits bits))
46+
(define r (random-bits bits prng))
3847
(if (r . >= . n) (loop) r))]))
3948

40-
(: random-integer (Integer Integer -> Integer))
41-
(define (random-integer a b)
49+
(: random-integer (case-> (Integer Integer -> Integer)
50+
(Integer Integer Pseudo-Random-Generator -> Integer)))
51+
(define (random-integer a b [prng (current-pseudo-random-generator)])
4252
(let ([a (min a b)] [b (max a b)])
43-
(+ a (random-natural (- b a)))))
53+
(+ a (random-natural (- b a) prng))))

math-test/math/tests/random-tests.rkt

+15
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
(require math/base)
33
(require typed/rackunit)
44

5+
(define g (current-pseudo-random-generator))
6+
57
(check-true
68
(for/or ([n (in-range 100)])
79
(> (random-natural (expt 2 64)) 4294967087)))
@@ -13,3 +15,16 @@
1315
(for ([n (in-range 100)])
1416
(define x (random-bits n))
1517
(check-true (and (<= 0 x) (< x (expt 2 n)))))
18+
19+
20+
(check-true
21+
(for/or ([n (in-range 100)])
22+
(> (random-natural (expt 2 64) g) 4294967087)))
23+
24+
(for ([n (in-range 100)])
25+
(define x (random-integer 1 100 g))
26+
(check-true (and (<= 1 x) (< x 100))))
27+
28+
(for ([n (in-range 100)])
29+
(define x (random-bits n g))
30+
(check-true (and (<= 0 x) (< x (expt 2 n)))))

0 commit comments

Comments
 (0)