Examples
Example 11. Pseudorandom Number Generator (PRNG)
This example is a pseudorandom number generator based on the Mersenne Twister, a uniform pseudorandom number generating algorithm developped by Makoto Matsumoto (松本 眞) and Takuji Nishimura (西村 拓士). The algorithm has a period of 219937-1 and according to Wikipedia, is the most widely used PRNG.
The implmentation presented here, MT19937, is based upon the Mersenne prime value of 19937 (a Mersenne prime has a value of 2n-1). This version uses a 32 bit word.
Some of the features of the Mersenne Twister PRNG are that it is fast, has a very long period, and passes numerous tests for statistical randomness. To test the speed of this implementation, I ran multiple tests, generating n 31-bit numbers. N ranged from 1,000 to 2 billion. The results are shown in the table and graph below.
| CPU Time (s) No. |
Numbers Generated |
CPU sec/ /Number |
μs /Number |
|---|---|---|---|
| 0.01 | 1,000 | 0.000010000 | 10.00 |
| 0.02 | 25,000 | 0.000000800 | 0.80 |
| 0.02 | 50,000 | 0.000000400 | 0.40 |
| 0.02 | 75,000 | 0.000000267 | 0.27 |
| 0.02 | 100,000 | 0.000000200 | 0.20 |
| 0.03 | 250,000 | 0.000000120 | 0.12 |
| 0.07 | 750,000 | 0.000000093 | 0.09 |
| 0.09 | 1,000,000 | 0.000000090 | 0.09 |
| 0.75 | 10,000,000 | 0.000000075 | 0.08 |
| 7.43 | 100,000,000 | 0.000000074 | 0.07 |
| 18.49 | 250,000,000 | 0.000000074 | 0.07 |
| 74.05 | 1,000,000,000 | 0.000000074 | 0.07 |
| 148.11 | 2,000,000,000 | 0.000000074 | 0.07 |
Testing shows that there is some "startup" overhead for this implementation. Between 1,000 and 250,000 generated numbers, the CPU time was between 0.01 and 0.02 seconds. After that the CPU time starts to grow in a more linear fashion. Note that the CPU time is that recorded from the job log output and not from SMF records, so there is a limitation to the accuracy of the recorded values. For small values of n, each generated number took about 10 microseconds. For larger values of n, the number drops to 0.07 microseconds or 70 nanoseconds.
Chart of Generated Numbers versus CPU Time

Test Statistics
Some simple statistics on the 1 million generated numbers are:
| Statistic | Value |
|---|---|
| Mean | 1,073,012,722 |
| Median | 1,073,240,357 |
| Skewness | 0.000737755 |
| Range | 2,147,481,371 |
| Minimum | 1,993 |
| Maximum | 2,147,483,364 |
| Count | 1,000,000 |
The MT generator prooduces a uniform distribution of random numbers. To test that claim, I produced a histogram of the 1 million numbers generated by the test. The chart below shows the distribution of the generated numbers. Visually, the sequence appears uniform.

C Source Code
#include <stdlib.h>
/*
This is a Mersenne Twister pseudorandom number generator
with p0000000**19937-1 with improved initialization scheme,
modified on 2002/1/26 by Takuji Nishimura and Makoto Matsumoto.
1. Initialization
This version (2002/1/26) has two initialization schemes:
init_genrand(seed) and init_by_array(init_key, key_length).
init_genrand(seed) initializes the state vector by using
one unsigned 32-bit integer "seed", which may be zero.
init_by_array(init_key, key_length) initializes the state vector
by using an array init_key[] of unsigned 32-bit integers
of length key_kength. If key_length is smaller than 624,
then each array of 32-bit integers gives distinct initial
state vector. This is useful if you want a larger seed space
than 32-bit word.
2. Generation
After initialization, the following type of pseudorandom numbers
The Mersenne Twister is a pseudorandom number generator (PRNG). It is a widely used PRNG.
Its name derives from the fact that its period length is chosen to be a Mersenne prime.
The Mersenne Twister was developed in 1997 by Makoto Matsumoto and Takuji Nishimura.
It was designed specifically to rectify most of the flaws found in older PRNGs.
It was the first PRNG to provide fast generation of high-quality pseudorandom integers.
The most commonly-used version of the Mersenne Twister algorithm is based on
the Mersenne prime 219937−1. The standard implementation of that, MT19937, uses
a 32-bit word length.
are available.
genrand_int32() generates unsigned 32-bit integers.
6. Reference
M. Matsumoto and T. Nishimura,
"Mersenne Twister: A 623-Dimensionally Equidistributed Uniform
Pseudo-Random Number Generator",
ACM Transactions on Modeling and Computer Simulation,
Vol. 8, No. 1, January 1998, pp 3--30.
-------
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
Advantages
The commonly-used version of Mersenne Twister, MT19937, which produces a sequence of
32-bit integers, has the following desirable properties:
It has a very long period of 219937 − 1.
While a long period is not a guarantee of quality in a random number generator,
short periods (such as the 232 common in many older software packages) can be problematic.
It is k-distributed to 32-bit accuracy for every 1 ≤ k ≤ 623 (see definition below).
It passes numerous tests for statistical randomness, including the Diehard tests.
For a k-bit word length, the Mersenne Twister generates integers in the range [0, 2k−2].
A C-program for MT19937, with initialization improved 2002/1/26.
Coded by Takuji Nishimura and Makoto Matsumoto.
Before using, initialize the state by using init_genrand(seed)
or init_by_array(init_key, key_length).
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The names of its contributors may not be used to endorse or promote
products derived from this software without specific prior written
permission.
This software is provided by the copyright holders and contributors
"as is" and any express or implied warranties, including, but not
limited to, the implied warranties of merchantability and fitness for
a particular purpose are disclaimed. In no event shall the copyright owner or
contributors be liable for any direct, indirect, incidental, special,
exemplary, or consequential damages (including, but not limited to,
procurement of substitute goods or services; loss of use, data, or
profits; or business interruption) however caused and on any theory of
liability, whether in contract, strict liability, or tort (including
negligence or otherwise) arising in any way out of the use of this
software, even if advised of the possibility of such damage.
Any feedback is very welcome.
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
*/
/* Period parameters */
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
static unsigned long mt[N]; /* the array for the state vector */
static int mti = N + 1; /* mti==N+1 means mt[N] is not initialized */
/* initializes mt[N] with a seed */
void init_genrand(unsigned long s)
{
mt[0] = s & 0xffffffffUL;
for (mti = 1; mti < N; mti++)
{
mt[mti] = (1812433253UL * (mt[mti - 1] ^ (mt[mti - 1] >> 30)) + mti);
/* See Knuth TAOCP Vol 2. 3rd Ed. P.106 for multiplier.
In the previous versions, MSBs of the seed affect
only MSBs of the array mt[]. */
mt[mti] &= 0xffffffffUL;
}
}
/* initialize by an array with array-length
init_key is the array for initializing keys
key_length is its length */
void init_by_array(unsigned long init_key[], int key_length)
{
int i, j, k;
init_genrand(19650218UL);
i = 1;
j = 0;
k = (N > key_length ? N : key_length);
for (; k; k--)
{
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1664525UL))
+ init_key[j] + j; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++;
j++;
if (i >= N)
{
mt[0] = mt[N - 1];
i = 1;
}
if (j >= key_length) j = 0;
}
for (k = N - 1; k; k--)
{
mt[i] = (mt[i] ^ ((mt[i - 1] ^ (mt[i - 1] >> 30)) * 1566083941UL))
- i; /* non linear */
mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
i++;
if (i >= N)
{
mt[0] = mt[N - 1];
i = 1;
}
}
mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */
}
/* generates a random number on [0,0xffffffff]-interval */
unsigned long genrand_int32(void)
{
unsigned long y;
static unsigned long mag01[2] = {0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
if (mti >= N)
{
/* generate N words at one time */
int kk;
if (mti == N + 1)
{
/* if init_genrand() has not been called, a default initial seed is used */
init_genrand(5489UL);
}
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 & 0x1UL];
}
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 & 0x1UL];
}
y = (mt[N - 1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
mt[N - 1] = mt[M - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
mti = 0;
}
y = mt[mti++];
/* Tempering */
y ^= (y >> 11);
y ^= (y << 7) & 0x9d2c5680UL;
y ^= (y << 15) & 0xefc60000UL;
y ^= (y >> 18);
return y;
}
/* generates a random number on [0,0x7fffffff]-interval */
long genrand_int31(void)
{
return (long) (genrand_int32() >> 1);
}
/* generates a random number on [0,1]-real-interval */
double genrand_real1(void)
{
return genrand_int32()*(1.0 / 4294967295.0);
/* divided by 2^32-1 */
}
/* generates a random number on [0,1)-real-interval */
double genrand_real2(void)
{
return genrand_int32()*(1.0 / 4294967296.0);
/* divided by 2^32 */
}
/* generates a random number on (0,1)-real-interval */
double genrand_real3(void)
{
return (((double) genrand_int32()) + 0.5)*(1.0 / 4294967296.0);
/* divided by 2^32 */
}
/* generates a random number on [0,1) with 53-bit resolution*/
double genrand_res53(void)
{
unsigned long a = genrand_int32() >> 5, b = genrand_int32() >> 6;
return(a * 67108864.0 + b)*(1.0 / 9007199254740992.0);
}
/* These real versions are due to Isaku Wada, 2002/01/09 added */
int main(void)
{
int i;
int j;
int x;
init_genrand(623578);
j = 0;
for (i = 0;i < 1000000;i++)
{
x = genrand_int31();
j += x;
}
return 0;
}