📄 random.sml
字号:
(* random.sml * * COPYRIGHT (c) 1993 by AT&T Bell Laboratories. See COPYRIGHT file for details. * * This package implements a random number generator using a subtract-with-borrow * (SWB) generator as described in Marsaglia and Zaman, "A New Class of Random Number * Generators," Ann. Applied Prob. 1(3), 1991, pp. 462-480. * * The SWB generator is a 31-bit generator with lags 48 and 8. It has period * (2^1487 - 2^247)/105 or about 10^445. In general, these generators are * excellent. However, they act locally like a lagged Fibonacci generator * and thus have troubles with the birthday test. Thus, we combine this SWB * generator with the linear congruential generator (48271*a)mod(2^31-1). * * Although the interface is fairly abstract, the implementation uses * 31-bit ML words. At some point, it might be good to use 32-bit words. *)structure Random : RANDOM = struct structure A = Array structure LW = LargeWord structure W8A = Word8Array structure W8V = Word8Vector structure P = Pack32Big val << = Word31.<< val >> = Word31.>> val & = Word31.andb val ++ = Word31.orb val xorb = Word31.xorb infix << >> & ++ val nbits = 31 (* bits per word *) val maxWord : Word31.word = 0wx7FFFFFFF (* largest word *) val bit30 : Word31.word = 0wx40000000 val lo30 : Word31.word = 0wx3FFFFFFF val N = 48 val lag = 8 val offset = N-lag fun error (f,msg) = LibBase.failure {module="Random",func=f, msg=msg} val two2neg30 = 1.0/((real 0x8000)*(real 0x8000)) (* 2^~30 *) fun minus(x,y,false) = (x - y, y > x) | minus(x,y,true) = (x - y - 0w1, y >= x) datatype rand = RND of { vals : Word31.word A.array,(* seed array *) borrow : bool ref, (* last borrow *) congx : Word31.word ref, (* congruential seed *) index : int ref (* index of next available value in vals *) } (* We represent state as a string, starting with an initial * word acting as an magic cookie (with bit 0 determining the * value of borrow), followed by a word containing index and a word * containing congx, followed by the seed array. *) val numWords = 3 + N val magic : LW.word = 0wx72646e64 fun toString (RND{vals, borrow, congx, index}) = let val arr = W8A.array (4*numWords, 0w0) val word0 = if !borrow then LW.orb (magic, 0w1) else magic fun fill (src,dst) = if src = N then () else ( P.update (arr, dst, Word31.toLargeWord (A.sub (vals, src))); fill (src+1,dst+1) ) in P.update (arr, 0, word0); P.update (arr, 1, LW.fromInt (!index)); P.update (arr, 2, Word31.toLargeWord (!congx)); fill (0,3); Byte.bytesToString (W8A.extract (arr, 0, NONE)) end fun fromString s = let val bytes = Byte.stringToBytes s val _ = if W8V.length bytes = 4 * numWords then () else error ("fromString","invalid state string") val word0 = P.subVec (bytes, 0) val _ = if LW.andb(word0, 0wxFFFFFFFE) = magic then () else error ("fromString","invalid state string") fun subVec i = P.subVec (bytes, i) val borrow = ref (LW.andb(word0,0w1) = 0w1) val index = ref (LW.toInt (subVec 1)) val congx = ref (Word31.fromLargeWord (subVec 2)) val arr = A.array (N, 0w0 : Word31.word) fun fill (src,dst) = if dst = N then () else ( A.update (arr, dst, Word31.fromLargeWord (subVec src)); fill (src+1,dst+1) ) in fill (3, 0); RND{vals = arr, index = index, congx = congx, borrow = borrow} end (* linear congruential generator: * multiplication by 48271 mod (2^31 - 1) *) val a : Word31.word = 0w48271 val m : Word31.word = 0w2147483647 val q = m div a val r = m mod a fun lcg seed = let val left = a * (seed mod q) val right = r * (seed div q) in if left > right then left - right else (m - right) + left end (* Fill seed array using subtract-with-borrow generator: * x[n] = x[n-lag] - x[n-N] - borrow * Sets index to 1 and returns 0th value. *) fun fill (RND{vals,index,congx,borrow}) = let fun update (ix,iy,b) = let val (z,b') = minus(A.sub(vals,ix), A.sub(vals,iy),b) in A.update(vals,iy,z); b' end fun fillup (i,b) = if i = lag then b else fillup(i+1, update(i+offset,i,b)) fun fillup' (i,b) = if i = N then b else fillup'(i+1, update(i-lag,i,b)) in borrow := fillup' (lag, fillup (0,!borrow)); index := 1; A.sub(vals,0) end (* Create initial seed array and state of generator. * Fills the seed array one bit at a time by taking the leading * bit of the xor of a shift register and a congruential sequence. * The congruential generator is (c*48271) mod (2^31 - 1). * The shift register generator is c(I + L18)(I + R13). * The same congruential generator continues to be used as a * mixing generator with the SWB generator. *) fun rand (congy, shrgx) = let fun mki (i,c,s) = let val c' = lcg c val s' = xorb(s, s << 0w18) val s'' = xorb(s', s' >> 0w13) val i' = (lo30 & (i >> 0w1)) ++ (bit30 & xorb(c',s'')) in (i',c',s'') end fun iterate (0, v) = v | iterate (n, v) = iterate(n-1, mki v) fun mkseed (congx,shrgx) = iterate (nbits, (0w0,congx,shrgx)) fun genseed (0,seeds,congx,_) = (seeds,congx) | genseed (n,seeds,congx,shrgx) = let val (seed,congx',shrgx') = mkseed (congx,shrgx) in genseed(n-1,seed::seeds,congx',shrgx') end val congx = ((Word31.fromInt congy & maxWord) << 0w1)+0w1 val (seeds,congx) = genseed(N,[],congx, Word31.fromInt shrgx) in RND{vals = A.fromList seeds, index = ref 0, congx = ref congx, borrow = ref false} end (* Get next random number. The tweak function combines * the number from the SWB generator with a number from * the linear congruential generator. *) fun randWord (r as RND{vals, index,congx,...}) = let val idx = !index fun tweak i = let val c = lcg (!congx) in congx := c; xorb(i, c) end in if idx = N then tweak(fill r) else tweak(A.sub(vals,idx)) before index := idx+1 end fun randInt state = Word31.toIntX(randWord state) fun randNat state = Word31.toIntX(randWord state & lo30) fun randReal state = (real(randNat state) + real(randNat state) * two2neg30) * two2neg30 fun randRange (i,j) = if j < i then error ("randRange", "hi < lo") else let val R = two2neg30*real(j - i + 1) in fn s => i + trunc(R*real(randNat s)) end handle _ => let val ri = real i val R = (real j)-ri+1.0 in fn s => trunc(ri + R*(randReal s)) end end; (* Random *)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -