Bicker x 2
Bicker x 2

Reputation: 137

Python27: random() after a setstate() doesn't produce the same random number

I have been subclassing an Python's random number generator to make a generator that doesn't repeat results (it's going to be used to generate unique id's for a simulator) and I was just testing to see if it was consistent in it's behavior after it has been loaded from a previours state

Before people ask:

Below is my test code:

def test_stateSavingConsitantcy(self):
    start = int(self.r.random())
    for i in xrange(start):
        self.r.random()
    state = self.r.getstate()
    next = self.r.random()
    self.r.setstate(state)
    nnext = self.r.random()
    self.assertEqual(next, nnext, "Number generation not constant got {0} expecting {1}".format(nnext,next))

Any help that can be provided would greatly appreciated

EDIT:

Here is my subclass as requested

class Singleton(type):
    _instances = {}
    def __call__(self, *args, **kwargs):
        if self not in self._instances:
            self._instances[self] = super(Singleton,self).__call__(*args,**kwargs)
        return self._instances[self]

class nrRand(Random):
    __metaclass__ = Singleton
    '''
    classdocs
    '''


    def __init__(self):
        '''
        Constructor
        '''
        super(nrRand,self).__init__()
        self.previous = []

    def random(self):
        n = super(nrRand,self).random()
        while n in self.previous:
            n = super(nrRand,self).random()
        self.previous.append(n)
        return n


    def seed(self,x):
        if x is None:
            x = long(time.time()*1000)
        self.previous = []
        count = x
        nSeed = 0
        while count < 0:
            nSeed = super(nrRand,self).random()
            count -= 1
        super(nrRand,self).seed(nSeed)

        while nSeed < 0:
            super(nrRand,self).seed(nSeed)
            count -= 1

    def getstate(self):
        return (self.previous, super(nrRand,self).getstate())

    def setstate(self,state):
        self.previous = state[0]
        super(nrRand,self).setstate(state[1])

Upvotes: 0

Views: 630

Answers (1)

user2357112
user2357112

Reputation: 280837

getstate and setstate only manipulate the state the Random class knows about; neither method knows that you also need to roll back the set of previously-generated numbers. You're rolling back the state inherited from Random, but then the object sees that it's already produced the next number and skips it. If you want getstate and setstate to work properly, you'll have to override them to set the state of the set of already-generated numbers.

UPDATE:

    def getstate(self):
        return (self.previous, super(nrRand,self).getstate())

This shouldn't directly use self.previous. Since you don't make a copy, you're returning the actual object used to keep track of what numbers have been produced. When the RNG produces a new number, the state returned by getstate reflects the new number. You need to copy self.previous, like so:

    def getstate(self):
        return (self.previous[:], super(nrRand, self).getstate())

I also recommend making a copy in setstate:

    def setstate(self, state):
        previous, parent_state = state
        self.previous = previous[:]
        super(nrRand, self).setstate(parent_state)

Upvotes: 2

Related Questions