Java Apache Math3 MersenneTwister VS Python random

I have a task to port my python code to Scala for research purposes. I am now using the Apache Math3 community library and am having difficulty with MersenneTwister.

In Python:

SEED = 1234567890

PRIMARY_RNG = random.Random()
PRIMARY_RNG.seed(SEED)
n = PRIMARY_RNG.randrange((2**31) - 1) #1977150888

      

In Scala:

val Seed = 1234567890
val PrimaryRNG = new MersenneTwister(Seed)
val n = PrimaryRNG.nextInt(Int.MaxValue) //1328851649

      

What am I missing here? Both are MersenneTwister,
andInt.MaxValue = 2147483647 = (2**31) - 1

+4


source to share


3 answers


As I wrote in the comments, the basic algorithm for getting the next integer between Python and Apache Math (source code here , here , and here ). Tracing through the code it seems that the main difference is how the two versions seed the generator. The Python version converts a given seed to an array and a seed from an array, while the Apache Math version has a separate algorithm for single-number seeding. Thus, for the Apache Math method to nextInt(...)

act as a Python method randrange(...)

, you must use the array version of Apache Math.

(I don't know Scala, so the following code is in Java)



MersenneTwister rng = new MersenneTwister();
rng.setSeed(new int[] {1234567890});
System.out.println(rng.nextInt(Integer.MAX_VALUE)); // 1977150888

      

Note also that all other methods like random()

vs. nextDouble()

are completely different, so this seeding mechanism will probably only work to do nextInt(...)

and randrange(...)

to return the same results.

+2


source


Apache Commons Math seems to use integer as its underlying source of randomness , although I'm not entirely sure how it retrieves it, and Python uses the double generated by the C version of the algorithm .



There can also be differences in how the initial values ​​are handled, but since they don't even read the bits in the same way, they cannot be expected to be comparable even if the underlying pseudo-random generator is the same.

+2


source


In case anyone needs to do this, I have developed a working version based on the CPython implementation .

Note: if you random.seed()

string, random.seed()

changes between Python 2 and 3. the pythonStringHash

function is pythonStringHash

compatible with Python 2, or in Python 3 - random.seed(s, version=1)

.

private static long pythonStringHash(String s) {
  char[] chars = s.toCharArray();
  long x;
  if (s.isEmpty()) {
    x = 0;
  } else {
    x = chars[0] << 7;
  }

  for (char c : chars) {
    x = ((1000003 * x) ^ c);
  }

  x ^= chars.length;
  if (x == -1) {
    return -2;
  }
  return x;
}

private static void pythonSeed(MersenneTwister random, long seed) {
  int[] intArray;
  if (Long.numberOfLeadingZeros(seed) >= 32) {
    intArray = new int[] { (int) seed };
  } else {
    intArray = new int[] { (int) seed, (int) (seed >> 32) };
  }
  random.setSeed(intArray);
}

public static RandomGenerator pythonSeededRandom(String seed) {
  MersenneTwister random = new MersenneTwister();
  pythonSeed(random, pythonStringHash(seed));
  return random;
}

      

From there pythonSeededRandom("foo").nextDouble()

should be equal random.seed("foo"); random.random()

random.seed("foo"); random.random()

.

0


source







All Articles