Search code examples
pythonrandommt19937

How is mt19937 prng exactly used for python random module functions?


from random import *
seed(5489)
hex(getrandbits(32)) # '0xc9a0e034'
hex(getrandbits(32)) # '0x38feb21f'

I ran this with Python 3.8.2 (not that it should matter too much). This is not what I would expect from a MT19937 32 bit PRNG. To be exact, I was expecting values similar to the ones presented in this website: https://create.stephan-brumme.com/mersenne-twister/

What is Python doing differently from other languages? Is there a way that I could reproduce the bits generated by Python myself? (Also, how is the random() float from 0 to 1 generated, from the 32-bit word sized integers?)

Thanks!


Solution

  • Python's seeding algorithm is nothing like the one the link uses. The Twister has a massive state, and a seed of only 32 bits can only put it into a comparatively trivial number of its possible states. Python's seeding algorithm uses every bit of arbitrarily large arguments.

    To reproduce Python's seeding algorithm, you'd have to read the C code and emulate it yourself. Nothing about it is defined.

    Python's code to generate an IEEE double is the same as the genrand_res53() function in the Twister's original C code:

    uint32_t a=genrand_uint32(self)>>5, b=genrand_uint32(self)>>6;
    return PyFloat_FromDouble((a*67108864.0+b)*(1.0/9007199254740992.0));
    

    In effect, the top 27 bits of one 32-bit Twister output are shifted left 26, and the top 26 bits of "the next" 32-bit Twister output fill in the lower 26 bits, giving a 53-bit value that's divided by 2.0**53.