Skip to content

Instantly share code, notes, and snippets.

@EmilySamantha80
Created May 12, 2025 19:58
Show Gist options
  • Select an option

  • Save EmilySamantha80/4029c9d40832b05d284ad8c4205d9620 to your computer and use it in GitHub Desktop.

Select an option

Save EmilySamantha80/4029c9d40832b05d284ad8c4205d9620 to your computer and use it in GitHub Desktop.
Cross Platform Mersenne Twister RNG (with seeding ability)

Cross-Platform Mersenne Twister Implementation

This is a cross-platform implementation of the Mersenne Twister pseudorandom number generator algorithm (MT19937) that produces identical sequences in JavaScript, Python, and C# when initialized with the same seed.

Features

  • Consistent random number generation across JavaScript, Python, and C#
  • Support for seeding to reproduce random sequences
  • High-quality randomness with a period of 2^19937-1
  • API for integers, floats and ranges

Implementation Details

The implementation is based on the original Mersenne Twister algorithm by Makoto Matsumoto and Takuji Nishimura. All three implementations follow the same algorithm with identical constants and operations to ensure they produce the same sequences given the same seed.

Usage Examples

JavaScript

// Import the class
const MersenneTwister = require('./MersenneTwister');

// Create a new instance with a seed
const rng = new MersenneTwister(12345);

// Generate random values
console.log(rng.nextInt32());  // Random 32-bit integer
console.log(rng.random());     // Random float in [0,1)
console.log(rng.randInt(1, 100)); // Random integer between 1 and 100
console.log(rng.randDouble(0, 10)); // Random float between 0 and 10

// Reset the seed
rng.seed(67890);

Python

# Import the class
from MersenneTwister import MersenneTwister

# Create a new instance with a seed
rng = MersenneTwister(12345)

# Generate random values
print(rng.next_int32())        # Random 32-bit integer
print(rng.random())            # Random float in [0,1)
print(rng.rand_int(1, 100))    # Random integer between 1 and 100
print(rng.rand_double(0, 10))  # Random float between 0 and 10

# Reset the seed
rng.seed(67890)

C#

// Import the namespace
using MersenneTwisterRNG;

// Create a new instance with a seed
var rng = new MersenneTwister(12345);

// Generate random values
Console.WriteLine(rng.NextInt32());        // Random 32-bit integer
Console.WriteLine(rng.Random());           // Random float in [0,1)
Console.WriteLine(rng.RandInt(1, 100));    // Random integer between 1 and 100
Console.WriteLine(rng.RandDouble(0, 10));  // Random float between 0 and 10

// Reset the seed
rng.Seed(67890);
using System;
namespace MersenneTwisterRNG
{
/// <summary>
/// A cross-platform Mersenne Twister pseudorandom number generator implementation that produces
/// identical sequences in JavaScript, Python, and C# when initialized with the same seed.
///
/// Based on the original Mersenne Twister algorithm by Makoto Matsumoto and Takuji Nishimura.
/// </summary>
public class MersenneTwister
{
private const int N = 624;
private const int M = 397;
private const uint MATRIX_A = 0x9908b0df;
private const uint UPPER_MASK = 0x80000000;
private const uint LOWER_MASK = 0x7fffffff;
private uint[] mt;
private int mti;
/// <summary>
/// Creates a new Mersenne Twister random number generator
/// </summary>
/// <param name="seed">The seed value (default: current timestamp)</param>
public MersenneTwister(uint? seed = null)
{
mt = new uint[N];
mti = N + 1;
// Set seed
Seed(seed ?? (uint)DateTimeOffset.UtcNow.ToUnixTimeSeconds());
}
/// <summary>
/// Set a new seed for the generator
/// </summary>
/// <param name="seed">The new seed value</param>
public void Seed(uint seed)
{
mt[0] = seed;
for (int i = 1; i < N; i++)
{
// Using unchecked to handle overflow correctly for uint arithmetic
// This matches how other languages implement this algorithm
unchecked
{
mt[i] = 1812433253u * (mt[i - 1] ^ (mt[i - 1] >> 30)) + (uint)i;
}
}
mti = N;
}
/// <summary>
/// Get the next 32-bit integer in the sequence
/// </summary>
/// <returns>A random 32-bit integer</returns>
public uint NextInt32()
{
uint y;
uint[] mag01 = new uint[] { 0, MATRIX_A };
// Generate N words at one time
if (mti >= N)
{
int kk;
// If Seed() hasn't been called, use a default seed
if (mti == N + 1)
{
Seed(5489); // Default seed
}
for (kk = 0; kk < N - M; kk++)
{
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + M] ^ (y >> 1) ^ mag01[y & 0x1];
}
for (; kk < N - 1; kk++)
{
y = (mt[kk] & UPPER_MASK) | (mt[kk + 1] & LOWER_MASK);
mt[kk] = mt[kk + (M - N)] ^ (y >> 1) ^ mag01[y & 0x1];
}
y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1];
mti = 0;
}
y = mt[mti++];
// Tempering
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680;
y ^= (y << 15) & 0xefc60000;
y ^= (y >> 18);
return y;
}
/// <summary>
/// Get the next random float in the range [0, 1) with 32-bit resolution
/// </summary>
/// <returns>A random float between 0 (inclusive) and 1 (exclusive)</returns>
public double Random()
{
return NextInt32() * (1.0 / 4294967296.0); // divided by 2^32
}
/// <summary>
/// Get a random integer between min and max (inclusive)
/// </summary>
/// <param name="min">Minimum value</param>
/// <param name="max">Maximum value</param>
/// <returns>A random integer between min and max</returns>
public int RandInt(int min, int max)
{
min = (int)Math.Ceiling((double)min);
max = (int)Math.Floor((double)max);
return (int)Math.Floor(Random() * (max - min + 1)) + min;
}
/// <summary>
/// Get a random double between min and max (inclusive)
/// </summary>
/// <param name="min">Minimum value</param>
/// <param name="max">Maximum value</param>
/// <returns>A random floating point number between min and max</returns>
public double RandDouble(double min, double max)
{
return Random() * (max - min) + min;
}
}
}
/**
* A cross-platform Mersenne Twister pseudorandom number generator implementation that produces
* identical sequences in JavaScript, Python, and C# when initialized with the same seed.
*
* Based on the original Mersenne Twister algorithm by Makoto Matsumoto and Takuji Nishimura.
*/
class MersenneTwister {
/**
* Creates a new Mersenne Twister random number generator
* @param {number} seed The seed value (default: current timestamp)
*/
constructor(seed = undefined) {
// MT19937 constants
this.N = 624;
this.M = 397;
this.MATRIX_A = 0x9908b0df;
this.UPPER_MASK = 0x80000000;
this.LOWER_MASK = 0x7fffffff;
// Initialize state
this.mt = new Array(this.N);
this.mti = this.N + 1;
// Set seed
this.seed(seed !== undefined ? seed : Math.floor(Date.now()));
}
/**
* Set a new seed for the generator
* @param {number} seed The new seed value
*/
seed(seed) {
seed = Math.floor(seed) >>> 0;
this.mt[0] = seed;
for (let i = 1; i < this.N; i++) {
// Use Math.imul for correct 32-bit signed multiplication, then mask to 32 bits
this.mt[i] = (Math.imul(1812433253, this.mt[i - 1] ^ (this.mt[i - 1] >>> 30)) + i) >>> 0;
}
this.mti = this.N;
}
/**
* Get the next 32-bit integer in the sequence
* @returns {number} A random 32-bit integer
*/
nextInt32() {
let y;
const mag01 = [0, this.MATRIX_A];
// Generate N words at one time
if (this.mti >= this.N) {
let kk;
// If seed() hasn't been called, use a default seed
if (this.mti === this.N + 1) {
this.seed(5489); // Default seed
}
for (kk = 0; kk < this.N - this.M; kk++) {
y = (this.mt[kk] & this.UPPER_MASK) | (this.mt[kk + 1] & this.LOWER_MASK);
this.mt[kk] = this.mt[kk + this.M] ^ (y >>> 1) ^ ((y & 0x1) ? this.MATRIX_A : 0);
}
for (; kk < this.N - 1; kk++) {
y = (this.mt[kk] & this.UPPER_MASK) | (this.mt[kk + 1] & this.LOWER_MASK);
this.mt[kk] = this.mt[kk + (this.M - this.N)] ^ (y >>> 1) ^ ((y & 0x1) ? this.MATRIX_A : 0);
}
y = (this.mt[this.N - 1] & this.UPPER_MASK) | (this.mt[0] & this.LOWER_MASK);
this.mt[this.N - 1] = this.mt[this.M - 1] ^ (y >>> 1) ^ ((y & 0x1) ? this.MATRIX_A : 0);
this.mti = 0;
}
y = this.mt[this.mti++];
// Tempering (matching the exact algorithm in the C# implementation)
y ^= y >>> 11;
y ^= (y << 7) & 0x9d2c5680;
y ^= (y << 15) & 0xefc60000;
y ^= y >>> 18;
return y >>> 0;
}
/**
* Get the next random float in the range [0, 1) with 32-bit resolution
* @returns {number} A random float between 0 (inclusive) and 1 (exclusive)
*/
random() {
return this.nextInt32() * (1.0 / 4294967296.0); // divided by 2^32
}
/**
* Get a random integer between min and max (inclusive)
* @param {number} min Minimum value
* @param {number} max Maximum value
* @returns {number} A random integer between min and max
*/
randInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(this.random() * (max - min + 1)) + min;
}
/**
* Get a random double between min and max (inclusive)
* @param {number} min Minimum value
* @param {number} max Maximum value
* @returns {number} A random floating point number between min and max
*/
randDouble(min, max) {
return this.random() * (max - min) + min;
}
}
// Exposing the module in Node.js or as a global in browser
if (typeof module !== 'undefined' && module.exports) {
module.exports = MersenneTwister;
} else {
window.MersenneTwister = MersenneTwister;
}
"""
A cross-platform Mersenne Twister pseudorandom number generator implementation that produces
identical sequences in JavaScript, Python, and C# when initialized with the same seed.
Based on the original Mersenne Twister algorithm by Makoto Matsumoto and Takuji Nishimura.
"""
import time
import math
class MersenneTwister:
"""
A Mersenne Twister random number generator that works consistently with JavaScript and C#
"""
def __init__(self, seed=None):
"""
Initialize a new Mersenne Twister RNG with an optional seed.
If no seed is provided, the current time is used.
Args:
seed (int, optional): The seed value. Defaults to current timestamp.
"""
# MT19937 constants
self.N = 624
self.M = 397
self.MATRIX_A = 0x9908b0df
self.UPPER_MASK = 0x80000000
self.LOWER_MASK = 0x7fffffff
# Initialize state
self.mt = [0] * self.N
self.mti = self.N + 1
# Set seed
self.seed(seed if seed is not None else int(time.time()))
def seed(self, seed):
"""
Set a new seed for the generator
Args:
seed (int): The new seed value
"""
seed = int(seed)
self.mt[0] = seed & 0xffffffff # ensure 32-bit unsigned
for i in range(1, self.N):
self.mt[i] = (1812433253 * (self.mt[i-1] ^ (self.mt[i-1] >> 30)) + i) & 0xffffffff
self.mti = self.N
def next_int32(self):
"""
Get the next 32-bit integer in the sequence
Returns:
int: A random 32-bit integer
"""
mag01 = [0, self.MATRIX_A]
# Generate N words at one time
if self.mti >= self.N:
# If seed() hasn't been called, use a default seed
if self.mti == self.N + 1:
self.seed(5489) # Default seed
for kk in range(0, self.N - self.M):
y = (self.mt[kk] & self.UPPER_MASK) | (self.mt[kk+1] & self.LOWER_MASK)
self.mt[kk] = self.mt[kk+self.M] ^ (y >> 1) ^ mag01[y & 0x1]
for kk in range(self.N - self.M, self.N - 1):
y = (self.mt[kk] & self.UPPER_MASK) | (self.mt[kk+1] & self.LOWER_MASK)
self.mt[kk] = self.mt[kk+(self.M-self.N)] ^ (y >> 1) ^ mag01[y & 0x1]
y = (self.mt[self.N-1] & self.UPPER_MASK) | (self.mt[0] & self.LOWER_MASK)
self.mt[self.N-1] = self.mt[self.M-1] ^ (y >> 1) ^ mag01[y & 0x1]
self.mti = 0
y = self.mt[self.mti]
self.mti += 1
# Tempering
y ^= (y >> 11)
y ^= (y << 7) & 0x9d2c5680
y ^= (y << 15) & 0xefc60000
y ^= (y >> 18)
return y & 0xffffffff # ensure 32-bit unsigned
def random(self):
"""
Get the next random float in the range [0, 1) with 32-bit resolution
Returns:
float: A random float between 0 (inclusive) and 1 (exclusive)
"""
return self.next_int32() * (1.0 / 4294967296.0) # divided by 2^32
def rand_int(self, min_val, max_val):
"""
Get a random integer between min_val and max_val (inclusive)
Args:
min_val (int): Minimum value
max_val (int): Maximum value
Returns:
int: A random integer between min_val and max_val
"""
min_val = math.ceil(min_val)
max_val = math.floor(max_val)
return math.floor(self.random() * (max_val - min_val + 1)) + min_val
def rand_double(self, min_val, max_val):
"""
Get a random double between min_val and max_val (inclusive)
Args:
min_val (float): Minimum value
max_val (float): Maximum value
Returns:
float: A random floating point number between min_val and max_val
"""
return self.random() * (max_val - min_val) + min_val
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment