Search code examples
javascriptpythonbitwise-operatorsportingrandom-seed

Porting bespoke random generator from JS to Python 3


guys.

I have found here a very basic random generator with seeding feature. Have also attached the code as a snippet.

var m_w = 123456789;
var m_z = 987654321;
var mask = 0xffffffff;

// Takes any integer
function seed(i) {
    m_w = (123456789 + i) & mask;
    m_z = (987654321 - i) & mask;
}

// Returns number between 0 (inclusive) and 1.0 (exclusive),
// just like Math.random().
function random()
{


    m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask;
    m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask;

    var result = ((m_z << 16) + (m_w & 65535)) >>> 0;
    result /= 4294967296;
    return result;
}

seed(1);

console.log(random())
console.log(random())
console.log(random())
console.log(random())
console.log(random())

I'm trying to port it to Python 3 and aware that there're differences in bitwise operations since JS and Python behave differently with long integers.

The Python code so far is:

#!/usr/bin/python
import ctypes

m_w = 123456789
m_z = 987654321
mask = 0xFFFFFF

#JS returns 123456790, 987654320
def bespokeSeed(seed_):

    global m_w, m_z, mask

    m_w = m_w + seed_
    m_z = m_z - seed_

def bespokeRandom():

    global m_w, m_z, mask

    #JS returns 990784270, 945037883
    m_z = (36969 * (m_z & 65535 & (2**53-1)) + (m_z >> 16) & (2**53-1))
    m_w = (18000 * (m_w & 65535 & (2**53-1)) + (m_w >> 16) & (2**53-1))
    #JS returns 722346555
    result = ((ctypes.c_int(m_z << 16 ^ 0).value + ctypes.c_int(m_w & 65535 ^ 0).value) >> 0) / 4294967296
    return result

bespokeSeed(1)
print(bespokeRandom())
print(bespokeRandom())
print(bespokeRandom())
print(bespokeRandom())
print(bespokeRandom())

The JS code (if you call random() for five times) returns

0.16818441334180534
0.7648323038592935
0.1521853597369045
0.22241602488793433
0.4391189150046557

While Python gets

0.16818441334180534
-0.23516769614070654
0.1521853597369045
0.22241602488793433
0.4391189150046557

As you can see it partly works, but has some issues returning negative numbers. Any ideas on how to fix it?


Solution

  • I don't understand why you have ctypes in there at all. The issue is that Javascript integers are limited to 32 bits, whereas Python has infinite precision. This does what you want:

    import ctypes
    
    m_w = 123456789
    m_z = 987654321
    mask = 0xFFFFFFFF
    
    def bespokeSeed(seed_):
    
        global m_w, m_z
    
        m_w = m_w + seed_
        m_z = m_z - seed_
    
    def bespokeRandom():
    
        global m_w, m_z
    
        #JS returns 990784270, 945037883
        m_z = (36969 * (m_z & 65535) + (m_z >> 16)) & mask
        m_w = (18000 * (m_w & 65535) + (m_w >> 16)) & mask 
        result = ((m_z << 16 & mask) + (m_w & 65535)) / 4294967296
        return result
    
    bespokeSeed(1)
    print(bespokeRandom())
    print(bespokeRandom())
    print(bespokeRandom())
    print(bespokeRandom())
    print(bespokeRandom())