"""Linux Random Device Support This module uses the standard random module and the linux random devices to provide truly random numbers with the usual python interface. Recomended Usage: 'from lrandom import random' or 'from lrandom import urandom as random' random Truly random numbers, asynchronous (may block indifinetly). urandom Not quite truly random, but better than psudorandom (I belive), synchronous. """ # Written by Levi Aho # Some code culled from "$prefix/lib/python2.2/random.py" __all__ = ["Random","random","urandom"] import random as _random, struct as _struct, bisect as _bisect class Random(_random.Random): def __init__(self, truly=0): if truly: filename = '/dev/random' else: filename = '/dev/urandom' self.setstate(filename) _random.Random.__init__(self) def random(self): rand = _struct.unpack('Q', self._file.read(8))[0] return rand / float(2 ** 64) def weighted_choice(self, seq): """levi's bisect idea""" choices = [] weights = [0] for (choice, weight) in seq: choices.append(choice) weights.append(weights[-1] + weight) del weights[0] return choices[_bisect.bisect(weights, self.randrange(weights[-1]))] # While the linux random device can be seeded, user applications can't do # so, and do not need to; therfore seed and jumpahead raise an error. def seed(self, a=None): if a != None: raise NotImplimented def jumpahead(self, n): raise NotImplimented # Note that you can use the state functions could be (ab)used # to open an arbitrary file if desired. This could have some # interesting uses (for example, a pipe). def getstate(self): return self._filename def setstate(self, state): self._filname = state self._file = file(state, 'rb') # Set up one generator per random device. random = Random(1) random.__doc__ = """Truly random numbers, asynchronous (may block).""" urandom = Random(0) urandom.__doc__ = """Not truly random, but better than psudorandom (I belive), synchronous.""" ## -------------------- test program -------------------- def _test_generator(n, funccall): import time from math import sqrt as _sqrt print n, 'times', funccall code = compile(funccall, funccall, 'eval') sum = 0.0 sqsum = 0.0 smallest = 1e10 largest = -1e10 t0 = time.time() for i in range(n): x = eval(code) sum = sum + x sqsum = sqsum + x*x smallest = min(x, smallest) largest = max(x, largest) t1 = time.time() print round(t1-t0, 3), 'sec,', avg = sum/n stddev = _sqrt(sqsum/n - avg*avg) print 'avg %g, stddev %g, min %g, max %g' % \ (avg, stddev, smallest, largest) def _test(N=200): _test_generator(N, 'urandom.random()') _test_generator(N, 'urandom.normalvariate(0.0, 1.0)') _test_generator(N, 'urandom.lognormvariate(0.0, 1.0)') _test_generator(N, 'urandom.cunifvariate(0.0, 1.0)') _test_generator(N, 'urandom.expovariate(1.0)') _test_generator(N, 'urandom.vonmisesvariate(0.0, 1.0)') _test_generator(N, 'urandom.gammavariate(0.5, 1.0)') _test_generator(N, 'urandom.gammavariate(0.9, 1.0)') _test_generator(N, 'urandom.gammavariate(1.0, 1.0)') _test_generator(N, 'urandom.gammavariate(2.0, 1.0)') _test_generator(N, 'urandom.gammavariate(20.0, 1.0)') _test_generator(N, 'urandom.gammavariate(200.0, 1.0)') _test_generator(N, 'urandom.gauss(0.0, 1.0)') _test_generator(N, 'urandom.betavariate(3.0, 3.0)') _test_generator(N, 'urandom.paretovariate(1.0)') _test_generator(N, 'urandom.weibullvariate(1.0, 1.0)') # Jumpahead test removed... if __name__ == '__main__': _test()