00001 """
00002 r-250 random number generator... VERY fast, high quality random bit strings and floats
00003 implemented by Marcus Mendenhall, Vanderbilt University Free Electron Laser Center, nashville, TN USA
00004 Public Domain
00005 December, 2002
00006
00007 The code which follows is a pythonization of code derived from the following two sources:
00008
00009 DESCRIPTION:
00010 Shift-register Random Number Generator.
00011
00012 Copyleft (c) 1987, 1992. Eugene I Levin, A.F.Ioffe institute,
00013 St. Petersburg, Russia.
00014 E-mail: Levin@lh.ioffe.rssi.ru
00015
00016 This is a public domain program and may be freely distributed under the
00017 condition of remaining the original Copyleft information.
00018
00019 The author would appreciate to be informed about any improvements of
00020 these functions, or their implementations for any other computer system.
00021
00022 and:
00023 /* r250.c the r250 uniform random number algorithm
00024
00025 Kirkpatrick, S., and E. Stoll, 1981; "A Very Fast
00026 Shift-Register Sequence Random Number Generator",
00027 Journal of Computational Physics, V.40
00028
00029 also:
00030
00031 see W.L. Maier, DDJ May 1991
00032
00033
00034
00035 */
00036
00037 REALIZATION NOTES:
00038 This is a realization of standard shift-register algorithm for random
00039 bit streams:
00040 x[n] = x[n-q] XOR x[n-p] (1)
00041 where p and q satisfy the condition that the polynomial x^p + x^q + 1
00042 must be primitive on the GF(2) field. There are a few known pairs (p,
00043 q), p = 250 and q = 103 has been used.
00044
00045 In order to generate float numbers independent bit streams (1) are used
00046 for the each bit of mantissa.
00047
00048 Consecutive values of (1) are stored in array "buffer" of the length p.
00049 The index of the n-th member of (1) in buffer is
00050 i = n mod p (2)
00051 so that a new value x[n] always replaces x[n-p]. Substitution of Eq. (2)
00052 into Eq. (1) yields to the algorithm:
00053 x[i] = x[i] XOR x[i - p] when i >= p, (3a)
00054 x[i] = x[i] XOR x[i - p + q] when i < p. (3b)
00055
00056 The operations have been extended to include r521 and r250_521, per the recommendations of
00057 Heuer, Dunweg and Ferrenberg,
00058 "Considerations on Correlations in Shift-Register Pseudorandom Number Generators and Their Removal"
00059 by Marcus Mendenhall
00060
00061 """
00062 _rcsid="$Id: r250.py,v 1.10 2007/08/30 14:32:15 mendenhall Exp $"
00063
00064 import random
00065
00066 try:
00067 import numpy as Numeric
00068 numeric_int32=Numeric.int32
00069 numeric_float=Numeric.float
00070 except:
00071 import Numeric
00072 numeric_int32=Numeric.Int32
00073 numeric_float=Numeric.Float
00074
00075 import math
00076
00077 class ran_shift(random.Random):
00078 "generate shift-register based high-quality randoms"
00079 p,q = 250, 103
00080 def __init__(self, seed=None):
00081 self.floatscale=1.0/float(1L<<62)
00082 self.single_floatscale=1.0/float(1L<<31)
00083 self.seed(seed)
00084 self.mask32=Numeric.array(0x7ffffe00, numeric_int32)
00085
00086 def seed(self, seed=None):
00087 "seed from the built in RNG python provides, and then scramble results a little"
00088 random.Random.seed(self, seed)
00089 seeds=[
00090 int( ((long(math.floor(random.random()*32768.0)) << 16) & 0x7fff0000) | long(math.floor(random.random()*65536.0))) for i in range(self.p)]
00091 self.ranbuf=Numeric.array(seeds, numeric_int32)
00092
00093 for i in range(self.p):
00094 self.regenerate()
00095
00096 def regenerate(self):
00097 "generate the next block of randoms, quickly"
00098
00099
00100
00101
00102 self.ranbuf[:self.q]^=self.ranbuf[-self.q:]
00103 self.ranbuf[self.q:]^=self.ranbuf[:-self.q]
00104 self.counter=0
00105
00106 def next(self):
00107 "return the next 31 bits from the random pool"
00108 v=self.ranbuf[self.counter]
00109 self.counter+=1
00110 if self.counter==self.p:
00111 self.regenerate()
00112 return v
00113
00114 def getstate(self):
00115 return Numeric.array(self.ranbuf), self.counter
00116
00117 def setstate(self, state):
00118 self.ranbuf=Numeric.array(state[0])
00119 self.counter = state[1]
00120
00121 def random(self):
00122 "return a _really good_ double precision (52 bits significant) random on [0,1) (upper included bound = (1 - 2^-52) )"
00123 q1, q2 = self.next(), self.next()
00124 return float( (long(q1) << 31) | ( long(q2) & 0x7ffffe00L) ) *self.floatscale
00125
00126 def single_float_random(self):
00127 "return a good single-precision (32 bits significant) random on [0,1) (upper included bound = (1 - 2^-32) )"
00128 q1 = self.next()
00129 return float(q1)*self.single_floatscale
00130
00131 def fast_random_series(self, count):
00132 "return a series of 31-bit ints much more quickly than individual calls to next()."
00133 results=Numeric.zeros(count, numeric_int32)
00134 first=min(count, self.p-self.counter)
00135 results[:first]=self.ranbuf[self.counter:self.counter+first]
00136
00137 count-=first
00138 self.counter+=first
00139 if self.counter==self.p:
00140 self.regenerate()
00141 for i in range(count//self.p):
00142 results[first:first+self.p]=self.ranbuf
00143 first+=self.p
00144 count-=self.p
00145 self.regenerate()
00146 if count:
00147 results[first:]=self.ranbuf[:count]
00148 self.counter=count
00149 return results
00150
00151 def single_float_random_series(self, count):
00152 "return a series of good single-precision-quality (actually 31 bits embedded in double) randoms on [0,1).(upper included bound = (1 - 2^-31) ) \
00153 Note: conversion of this number to Float32 could result in rounding to exactly 1.0"
00154
00155
00156 q1 = Numeric.array(self.fast_random_series(count), numeric_float)
00157 q1*=self.single_floatscale
00158 return q1
00159
00160 def double_float_random_series(self, count):
00161 "return a series of 52-bit significance double-precision randoms on [0,1)."
00162 q1 = self.fast_random_series(2*count)
00163 q1[1::2] &= self.mask32
00164 q1=q1*self.single_floatscale
00165
00166 q1[1::2]*=self.single_floatscale
00167 q1[0::2]+=q1[1::2]
00168 return q1[::2]
00169
00170 class r250(ran_shift):
00171 "generate r250 based high-quality randoms"
00172 p,q = 250, 103
00173
00174 class r521(ran_shift):
00175 "generate r521 based high-quality randoms"
00176 p,q = 521, 168
00177
00178 class r250_521(r250):
00179 "generate super-quality r250/521 randoms per Heuer, Dunweg & Ferrenberg ca. 1995"
00180 def __init__(self, seed=None):
00181 r250.__init__(self, seed)
00182 self.r521=r521(seed)
00183
00184 def getstate(self):
00185 return r250.getstate(self), self.r521.getstate()
00186
00187 def setstate(self, state):
00188 r250.setstate(self, state[0])
00189 self.r521.setstate(state[1])
00190
00191 def next(self):
00192 return r250.next(self) ^ self.r521.next()
00193
00194 def fast_random_series(self, count):
00195 return r250.fast_random_series(self, count) ^ self.r521.fast_random_series(count)
00196
00197 if __name__ == "__main__":
00198 r=r250_521()
00199
00200 for i in range(20):
00201 print r.random(),
00202 print
00203
00204 print 10*"%08lx "%tuple(r.fast_random_series(10).tolist())
00205 print
00206
00207 state=r.getstate()
00208
00209 print r.single_float_random_series(10)
00210 print
00211
00212 print r.double_float_random_series(10)
00213 print
00214
00215 r.setstate(state)
00216 print r.single_float_random_series(10)
00217 print
00218
00219 r.setstate(state)
00220 print r.single_float_random_series(10)
00221 print
00222
00223 import struct
00224
00225 print "checking for uniformly filled bit in combined doubles"
00226 r.setstate(state)
00227 s=r.double_float_random_series(100).tostring()
00228 ss=struct.unpack("20L", s[:80])
00229 print (20*"%08lx ")%ss
00230 b1, b2 =0, 0
00231 for i,j in zip(ss[0::2], ss[1::2]):
00232 b1, b2 = b1 | i, b2 | j
00233 print "this should be 3fffffff ffffffff:", "%08lx %08lx" % (b1,b2)
00234
00235 r.setstate(state)
00236 ranlistfast=r.fast_random_series(1000).tolist()
00237 r.setstate(state)
00238 ranlist=[r.next() for i in range(1000)]
00239
00240 if ranlist != ranlistfast:
00241 print "fast series is not same as single randoms"
00242 print zip(ranlist, ranlistfast)
00243 else: print "fast series sequence is same as individual numbers"
00244
00245 r.setstate(state)
00246 ranlistfast=r.single_float_random_series(1000).tolist()
00247 r.setstate(state)
00248 ranlist=[r.single_float_random() for i in range(1000)]
00249
00250 if ranlist != ranlistfast:
00251 print "fast single series is not same as single randoms"
00252 print zip(ranlist, ranlistfast)
00253 else: print "fast single series sequence is same as individual numbers"
00254
00255 r.setstate(state)
00256 ranlistfast=r.double_float_random_series(1000).tolist()
00257 r.setstate(state)
00258 ranlist=[r.random() for i in range(1000)]
00259
00260 if ranlist != ranlistfast:
00261 print "fast double series is not same as single randoms"
00262 print zip(ranlist, ranlistfast)
00263 else: print "fast double series sequence is same as individual numbers"
00264
00265
00266 if 1:
00267 print "running pi-darts in 3d"
00268 cycles=100
00269 count=10000
00270 sum=0.0
00271 sum2=0.0
00272 for i in range(cycles):
00273 r1=r.single_float_random_series(3*count)
00274 r1*=r1
00275 insum=Numeric.sum(Numeric.less(r1[0::3]+r1[1::3]+r1[2::3], 1.0))
00276 pi=6.0*insum/count
00277 sum+=pi
00278 sum2+=pi*pi
00279 print pi, " ",
00280 print
00281 print "pi estimate = %.6f +- %.6f" % (sum/cycles, math.sqrt(sum2/cycles-(sum/cycles)**2)/math.sqrt(cycles) )
00282