
Mersenne Twister (MT19937): Period, Reproducibility, and Security Limits
Mersenne Twister (MT19937) is a pseudorandom number generator described in a 1998 paper by Makoto Matsumoto and Takuji Nishimura. Its long period and reproducibility are useful for simulations, but it is unsuitable for passwords or secret tokens. This guide separates period, state, and equidistribution, then demonstrates the sequence with runnable JavaScript. It does not imply that NanToo’s dice or draw tools use MT.
Japanese original published: 2026-04-28
The Japanese research behind the 1998 paper
Makoto Matsumoto and Takuji Nishimura introduced Mersenne Twister in their 1998 paper. The author manuscript lists Keio University and the Max Planck Institute for Mathematics for Matsumoto, and Keio University for Nishimura. Its present-day hosting on a Hiroshima University domain does not establish that the research originated there.
The authors date development to 1996–1997 and the improved initialization code to 2002. The original algorithm and the later initialization procedure are distinct. “Mersenne” refers to the Mersenne prime associated with the period; “Twister” refers to its twisted generalized feedback shift register design.
Physical entropy and pseudorandom generation are different layers
A pseudorandom number generator (PRNG) determines its next state and output from its current state. Fix the algorithm, initialization procedure, seed, and call order, and you can reproduce the sequence. The same seed alone does not guarantee matching results across languages or APIs.
Entropy obtained from physical noise and the generator that expands seeded state into outputs are different layers. A cryptographically secure PRNG (CSPRNG) also includes deterministic operations, but suitable seeding and design make its outputs difficult to predict. Determinism does not disqualify every generator from cryptographic use; MT19937 lacks the required cryptographic design.
Intel’s RDRAND instruction returns the output of a deterministic random bit generator (DRBG), not unprocessed physical noise. CPU and operating-system random APIs should not all be described as returning raw “true randomness.”
Period, stored state, and seed space
Scroll horizontally if the table does not fit.
| Quantity | Meaning for MT19937 |
|---|---|
| Period | 219937−1 for a nonzero effective state: approximately 4.3 × 106001 |
| Stored state representation | 624 words of 32 bits: 2,496 bytes, excluding the index and other bookkeeping |
| State dimension of the recurrence | 624 × 32 − 31 = 19,937 bits |
| One 32-bit seed | At most 232 initialization inputs, not one input for every position in the period |
The period measures how long the sequence runs before repeating. It does not mean that a short seed can select every possible initial state, nor does it prove that adjacent outputs are independent. APIs with a 32-bit seed, such as PHP’s Mt19937, have a limited seed space despite the generator’s enormous period.
What 623-dimensional equidistribution guarantees
MT19937 is 623-dimensionally equidistributed at 32-bit precision. Group 623 consecutive full 32-bit outputs together and move the starting position through the entire period: every possible bit pattern occurs equally often, except that the all-zero tuple occurs once fewer. That exception is part of the original definition.
When outputs are truncated to v bits, the corresponding equidistribution dimension k(v) must be considered separately. Bit precision and dimension are different quantities.
This property does not promise equal counts in a short sample, success on every statistical test, or cryptographic unpredictability. It also differs from the conditional uniformity required of each index selection in a Fisher–Yates shuffle.
Twisting and tempering with 32-bit arithmetic
The state update joins the upper bit of one word to the lower 31 bits of the next. It uses a right shift, a conditional XOR, and an offset of 397 words. Tempering applies shifts and masked XOR operations to an output word. This is a reversible bit transformation, not encryption.
The educational example below uses the 2002 initialization procedure for a single 32-bit seed. Arithmetic wraps modulo 232. In JavaScript, Math.imul supplies the low 32 bits of multiplication, and >>> performs an unsigned right shift. The twist updates the array sequentially in place; after wrapping around, it also reads words that have already been updated. Replacing this with a separate array computed entirely from the old block changes the sequence.
function mt19937(seed) {
if (!Number.isInteger(seed) || seed < 0 || seed > 0xffffffff) {
throw new RangeError("Expected an integer seed from 0 to 2^32-1");
}
const state = new Uint32Array(624);
state[0] = seed;
for (let i = 1; i < 624; i++) {
const previous = state[i - 1];
state[i] = (Math.imul(1812433253, previous ^ (previous >>> 30)) + i) >>> 0;
}
let cursor = 624;
return function nextUint32() {
if (cursor === 624) {
for (let i = 0; i < 624; i++) {
const joined = (state[i] & 0x80000000) | (state[(i + 1) % 624] & 0x7fffffff);
state[i] = (state[(i + 397) % 624] ^ (joined >>> 1) ^
((joined & 1) ? 0x9908b0df : 0)) >>> 0;
}
cursor = 0;
}
let value = state[cursor++];
value ^= value >>> 11;
value ^= (value << 7) & 0x9d2c5680;
value ^= (value << 15) & 0xefc60000;
return (value ^ (value >>> 18)) >>> 0;
};
}
const next = mt19937(5489);
console.log(Array.from({ length: 5 }, () => next()));
// [3499211612, 581869302, 3890346734, 3586334585, 545404204]The state is twisted before the first output and again after each block of 624 outputs. Each generator has its own state array and returns one unsigned 32-bit integer per call. This example is for learning: it is neither a cryptographic generator nor a universal match for every language’s seeding rules.
What to record for reproducibility
Record the generator, library and version, initialization procedure, seed or saved state, call order, and conversion into the desired range or distribution. An integer, a floating-point value, and a normally distributed value need not consume the same number of internal outputs.
Do not expect seed 5489 in this example to match Python’s random.seed(5489): their initialization procedures differ. Giving every parallel worker the same seed does not create independent streams. Check the library’s documented stream-splitting or spawning behavior.
Mapping an unsigned 32-bit output to an integer range by taking its remainder modulo n introduces bias when n does not divide 232. Selecting MT does not remove that bias. Evaluate the generator and the range-conversion method separately.
Where MT appears in language libraries
Scroll horizontally if the table does not fit.
| Environment | API and limitations |
|---|---|
| Python | random.Random uses MT; SystemRandom is different. Use secrets for secret values. |
| C++ | std::mt19937 explicitly selects the 32-bit engine. Do not assume default_random_engine selects MT. |
| PHP | mt_rand and Random\Engine\Mt19937. Check initialization and compatibility modes for behavior before PHP 7.1. |
| R | The default RNGkind is Mersenne-Twister. Conversion settings such as sample.kind also affect reproducibility. |
| Ruby | Random uses a modified MT. Do not assume seed compatibility with the reference C implementation. |
| MATLAB | The factory default on the client is twister. Check changed settings, parallel workers, and GPU generators separately. |
| NumPy | RandomState uses legacy MT. Generator supports different bit generators; default_rng currently defaults to PCG64. |
Widespread adoption does not establish that an algorithm is best for every task. Historical descriptions also do not justify claims about every current Excel version or an undocumented implementation.
Why MT is unsuitable for secrets
MT19937 is not cryptographically secure. Its state transition is linear and tempering is reversible, so a recovered state predicts later outputs. For example, 624 consecutive full 32-bit output words aligned with a state-update block allow the state to be reconstructed by reversing tempering.
This is not an unconditional claim that any 624 observations always suffice. Bounded integers, floating-point values assembled from multiple words, missing outputs, and intervening calls change what an observer sees. None of these transformations establishes that MT is secure.
For passwords, secret tokens, and keys, use an appropriate cryptographic API, such as browser crypto.getRandomValues(), Node.js crypto.randomBytes(), Python’s secrets, or PHP’s random_bytes(). Follow the protocol’s requirements for nonces: uniqueness and unpredictability are distinct properties, and MT does not automatically provide both.
Web Crypto requires cryptographically strong random values without prescribing a specific internal generator. Silently falling back to Math.random() after a failure breaks the security requirement for secret values.
Comparing alternatives without a universal ranking
SFMT is an MT-family generator designed with SIMD in mind. PCG and xoshiro families make different design choices. They cannot meaningfully be ranked as a single line of successors.
Compare the target CPU, compiler, integer width, distribution conversion, parallel streams, statistical tests, reproducibility, and security requirements. A speed ratio without those conditions is not useful. Replacing MT with a faster noncryptographic PRNG does not provide cryptographic security.
Randomness in NanToo’s Japanese tools
The following table describes how NanToo’s current Japanese tools generate and use random values.
Scroll horizontally if the table does not fit.
| Tool | Random source and method | Purpose |
|---|---|---|
| Dice roller | crypto.getRandomValues() (CSPRNG) | Match the stated random-source contract and avoid modulo bias |
| Coin flip | crypto.getRandomValues() (CSPRNG) | Map one bit of a 32-bit value to heads or tails |
| Draw tool | crypto.getRandomValues(), rejection sampling, and Fisher–Yates | Map 32-bit values to swap indices without modulo bias |
| Roulette tool | crypto.getRandomValues() and rejection sampling | Map 32-bit values to candidate indices without modulo bias |
| Password generator | crypto.getRandomValues() and rejection sampling | Select characters without modulo bias |
These are implementation descriptions. Using Web Crypto alone does not establish equivalence to a physical draw, independently audited fairness, or compliance with legal requirements for lotteries or gambling.
A generator is not a shuffle or a probability model
The algorithm behind ECMAScript’s Math.random() is implementation-defined. Web Crypto does not specify MT either. The table above describes NanToo’s current Japanese tools; those tools do not use the educational MT implementation shown in this article.
A generator produces a sequence of values. Fisher–Yates uses selected values to rearrange a list. A probability calculator does something different again: it computes a mathematical distribution under assumptions such as independent, uniform dice. Theoretical results for sums of dice or rolling four dice and dropping the lowest are not MT-specific output guarantees.
Choose by purpose. Reproducible experiments need a PRNG whose configuration you can record. Secret values need an appropriate CSPRNG. Unbiased range selection needs a suitable mapping method as well.
References and sources
- Matsumoto and Nishimura — Mersenne Twister (1998), author manuscript ↗
- Authors — What is Mersenne Twister? ↗
- Authors — Improved initialization (2002) ↗
- C++ working draft — mersenne_twister_engine ↗
- Python — random ↗
- PHP — Mt19937 constructor and seed limits ↗
- PHP — mt_rand ↗
- R — Random number generation ↗
- Ruby — Random ↗
- MATLAB — rng ↗
- NumPy — Random Generator ↗
- NumPy — MT19937 ↗
- Intel — Digital Random Number Generator guide ↗
- W3C Web Crypto API — Crypto.getRandomValues() ↗
- ECMAScript 2026 — Math.random() ↗
Editorial note
This article was prepared with AI assistance and reviewed by an editor before publication. It may still contain factual errors, interpretation mistakes, or outdated information. Check the cited primary sources or official documentation before making an important decision.

