-
Notifications
You must be signed in to change notification settings - Fork 3
/
random.lisp
254 lines (236 loc) · 9.76 KB
/
random.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
;;; -*- Mode: Lisp -*-
;;;
;;; $Header: /home/gene/library/website/docsrc/jmt/RCS/jmt.lisp,v 395.1 2008/04/20 17:25:47 gene Exp $
;;;
;;; Copyright (c) 2002, 2004 Jason Stover. All rights reserved.
;;;
;;; This program is free software; you can redistribute it and/or modify
;;; it under the terms of the GNU Lesser General Public License as
;;; published by the Free Software Foundation; either version 2.1 of the
;;; License, or (at your option) any later version.
;;;
;;; This program is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU Lesser General Public
;;; License along with this program; if not, write to the Free Software
;;; Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
;;; USA
;;; This is a implementation of the Mersenne Twister pseudo-random
;;; number generator by M. Matsumoto and T. Nishimura. See
;;; "Mersenne Twister: A 623-dimensionally equidistributed uniform
;;; pseudorandom number generator", ACM Transactions on Modeling and
;;; Computer Simulation vol. 8, no. 1, January 1998, pp 3-30.
;;; As of the time of this writing, the paper can also be found at
;;; www.math.keio.ac.jp/~matumoto/emt.html.
;;; LOG
;;;
;;; WARNING: I just finished this on 1/8/02 after learning
;;; just a bit of Lisp. I haven't tested it to make sure
;;; its generated values look uniformly distributed in high
;;; dimensions. Both a simple histogram and scatterplot of the
;;; values looked okay, but there could be problems. Feel free
;;; to fix them and tell me about your solutions. (JHS)
;;;
;;; 4 February 2004, Gene Michael Stover
;;; Rewrite & testing.
;;;
;;; 7 February 2004, Gene Michael Stover.
;;; Fixed two bugs found by Marc Battyani. In one of the ASSERTs,
;;; I had APPLYied #'AND as if it's a function, but AND is a macro.
;;; Also, I create the MAG01 array by COERCEing a list to an array.
;;; I guess Lispworks doesn't like that, but Mr. Battyani said it
;;; worked if the array was a 'vector. So I changed that. It runs
;;; fine on clisp 2.31 with the two fixes. I don't have any other
;;; Lisp installed, though, so I can't conveniently test it on others.
;;;
;;; 8 February 2004. Gene Michael Stover.
;;; Renamed a couple of constants to begin with "mt-" to avoid
;;; name collisions.
;;; MT-RANDOM now works when its "limit" argument is an integral
;;; bignum. In other words, "(mt-random (expt 2 68))" should
;;; return (- (expt 2 68) 1) with the same frequency it returns
;;; any other number.
(in-package #:cl-skip-list)
;;; These two constants should begin with "*mt-" to help avoid
;;; collisions. (2004-Feb-07, gms)
(defconstant *mt-k2^32* (expt 2 32))
(defconstant *mt-k-inverse-2^32f* (expt 2.0 -32.0)
"1/(2^32), as a floating-point number")
(defconstant *mt-n* 624)
(defconstant *mt-m* 397)
(defconstant *mt-upper-mask* #x80000000 "most significant w-r bits")
(defconstant *mt-lower-mask* #x7FFFFFFF "least significant r bits")
(defstruct (mt-random-state
(:constructor mt-internal-make-random-state))
;; Could have avoided MTI, which is an index into ARR, with a
;; fill pointer in ARR. MTI more closely follows the reference
;; implementation.
;; ARR corresponts to "mt[]" in the reference implementation.
;; Probably should have called it MT after all. Oh well.
mti ; index into ARR
arr) ; array of numbers
(labels
((next-seed (n) (mod (1+ (* 69069 n)) *mt-k2^32*))
(get-hi16 (n) (logand n #xFFFF0000))
(next-elt (n)
(logior (get-hi16 n)
(ash (get-hi16 (next-seed n)) -16))))
(defun mt-make-random-state-integer (n)
"Use the single integer to expand into a bunch of
integers to use as an MT-RANDOM-STATE.
Copied from the 'sgenrand' function in mt19937int.c.
This is mostly an internal function. I recommend using
MAKE-MT-RANDOM-STATE unless specific circumstances dictate otherwise."
(mt-internal-make-random-state
:mti *mt-n*
:arr (make-array
*mt-n*
:element-type 'integer
:initial-contents (do ((i 0 (1+ i))
(sd n (next-seed (next-seed sd)))
(lst () (cons (next-elt sd) lst)))
((>= i *mt-n*) (nreverse lst)))))))
(defvar *mt-random-state* nil
"Unlike the reference implementation, we'll initialize the random
state to a hopefully somewhat random & unique value., but not until after defining
(mt-make-random-state-random)")
(let ((some-number 0))
(defun mt-make-random-state-random ()
"Generate a new random state from a new, hopefully somewhat
random, value.
This is mostly an internal function. I recommend using
MAKE-MT-RANDOM-STATE unless specific circumstances dictate otherwise."
(mt-make-random-state-integer (+ (get-universal-time)
(incf some-number)))))
(defun make-mt-random-state (&optional state)
"Analogous to Common Lisp's MAKE-RANDOM-STATE except that this function
works on random states for JMT's Mersenne Twister implementation."
(cond ((eq state t) (mt-make-random-state-random))
((null state)
;; For NIL, return a copy of the current state.
(make-mt-random-state *mt-random-state*))
((integerp state)
;; Expand the integer STATE into controlled junk that is an
;; MT RANDOM STATE.
(mt-make-random-state-integer state))
((typep state 'sequence)
;; It's a list or an array. It must be of length *MT-N*, & it
;; must contain integers. We'll create a random state object
;; using a copy of that sequence.
(assert state) ; should have caught NIL earlier
(assert (eql (length state) *mt-n*))
(assert (not (find-if #'integerp state)))
(mt-internal-make-random-state
:mti 0
:arr (copy-seq (coerce state 'array))))
((mt-random-state-p state)
;; Return a copy of state. It is an instance of MT-RANDOM-STATE.
(mt-internal-make-random-state
:mti (mt-random-state-mti state)
:arr (copy-seq (mt-random-state-arr state))))
(t
;; For anything else, error.
(cerror "STATE should not have a value of ~A" state))))
(setq *mt-random-state* (make-mt-random-state t))
(let* ((matrix-a #x9908B0DF)
(mag01 (coerce (list 0 matrix-a) 'vector)))
(defun mt-refill ()
"In the C program mt19937int.c, there is a function called 'genrand', & in
that function there is a block of code to execute when the mt[] array is
exhausted. This function is that block of code. I've removed it from my
MT-GENRAND function for clarity."
;; This function is pretty much a direct translation of the C function.
;; In other words, you're about to see some very un-Lispy code.
(let (y kk)
(setq kk 0)
(do ()
((>= kk (- *mt-n* *mt-m*)))
(setq y (logior
(logand (aref (mt-random-state-arr *mt-random-state*)
kk)
*mt-upper-mask*)
(logand (aref (mt-random-state-arr *mt-random-state*)
(1+ kk))
*mt-lower-mask*)))
(setf (aref (mt-random-state-arr *mt-random-state*) kk)
(logxor
(aref (mt-random-state-arr *mt-random-state*) (+ kk *mt-m*))
(ash y -1)
(aref mag01 (logand y 1))))
(incf kk))
(do ()
((>= kk (- *mt-n* 1)))
(setq y (logior
(logand
(aref (mt-random-state-arr *mt-random-state*) kk)
*mt-upper-mask*)
(logand
(aref (mt-random-state-arr *mt-random-state*) (1+ kk))
*mt-lower-mask*)))
(setf (aref (mt-random-state-arr *mt-random-state*) kk)
(logxor (aref (mt-random-state-arr *mt-random-state*)
(+ kk (- *mt-m* *mt-n*)))
(ash y -1)
(aref mag01 (logand y 1))))
(incf kk))
(setq y (logior
(logand
(aref (mt-random-state-arr *mt-random-state*) (- *mt-n* 1))
*mt-upper-mask*)
(logand
(aref (mt-random-state-arr *mt-random-state*) 0)
*mt-lower-mask*)))
(setf (aref (mt-random-state-arr *mt-random-state*) (- *mt-n* 1))
(logxor
(aref (mt-random-state-arr *mt-random-state*) (- *mt-m* 1))
(ash y -1)
(aref mag01 (logand y 1))))
(setf (mt-random-state-mti *mt-random-state*) 0))
'mt-refill))
(defun mt-tempering-shift-u (n)
(mod (ash n -11) *mt-k2^32*))
(defun mt-tempering-shift-s (n)
(mod (ash n 7) *mt-k2^32*))
(defun mt-tempering-shift-t (n)
(mod (ash n 15) *mt-k2^32*))
(defun mt-tempering-shift-l (n)
(mod (ash n -18) *mt-k2^32*))
(let ((mt-tempering-mask-b #x9d2c5680)
(mt-tempering-mask-c #xefc60000))
(defun mt-genrand ()
(when (>= (mt-random-state-mti *mt-random-state*) *mt-n*)
(mt-refill))
(let ((y (aref (mt-random-state-arr *mt-random-state*)
(mt-random-state-mti *mt-random-state*))))
(incf (mt-random-state-mti *mt-random-state*))
;; The following separate, explicit SETQ & other expressions
;; could be compacted/optimized into a single arithmetic expression
;; that does not store into any temporary variables. That could be
;; more efficient at run-time, but I have chosen instead of immitate
;; the statements in the C program, mt19937int.c.
(setq y (logxor y (mt-tempering-shift-u y)))
(setq y (logxor y (logand (mt-tempering-shift-s y)
mt-tempering-mask-b)))
(setq y (logxor y (logand (mt-tempering-shift-t y)
mt-tempering-mask-c)))
(setq y (logxor y (mt-tempering-shift-l y)))
y)))
(defun mt-random (n &optional state)
"Generate a random number. WARNING: setting state here is not thread safe;
*mt-random-state* will be set without any regard for what others are doing with it!"
(assert (plusp n))
(when state
(assert (mt-random-state-p state))
;; Save a copy of the random state.
(setq *mt-random-state* (make-mt-random-state state)))
(if (integerp n)
(mod (do ((bits-needed (log n 2) )
(bit-count 0 (+ 32 bit-count))
(r 0 (+ (ash r 32) (mt-genrand))))
((>= bit-count bits-needed) r))
n)
(* (mt-genrand) *mt-k-inverse-2^32f* n)))
;;; --- end of file ---