Skip to content

Commit 60c1c56

Browse files
committed
Added initial implementation of Mersenne Twister
1 parent c9a12fc commit 60c1c56

File tree

3 files changed

+133
-3
lines changed

3 files changed

+133
-3
lines changed

src/main/java/org/thejavaguy/prng/generators/MersenneTwister.java

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
package org.thejavaguy.prng.generators;
22

33
public class MersenneTwister implements PRNG {
4+
private static final int N = 624;
5+
private static final int M = 397;
6+
private static final int INIT_MULTIPLICATOR = 1_812_433_253;
7+
private static final int MATRIX_A = 0x9908b0df;
8+
private static final int UPPER_MASK = 0x80000000;
9+
private static final int LOWER_MASK = 0x7fffffff;
10+
private static final int TEMPERING_MASK_B = 0x9d2c5680;
11+
private static final int TEMPERING_MASK_C = 0xefc60000;
12+
private static final int DEFAULT_SEED = 5489;
13+
private int[] mt;
14+
private int mti;
15+
private int[] mag01;
16+
17+
public MersenneTwister() {
18+
this(DEFAULT_SEED);
19+
}
20+
21+
public MersenneTwister(int seed) {
22+
if (seed == 0) {
23+
throw new IllegalArgumentException("seed must not be 0");
24+
}
25+
mt = new int[N];
26+
mt[0] = seed;
27+
for (mti = 1; mti < N; ++mti) {
28+
mt[mti] = (INIT_MULTIPLICATOR * (mt[mti-1] ^ (mt[mti-1] >>> 30)) + mti);
29+
}
30+
mag01 = new int[2];
31+
mag01[0] = 0;
32+
mag01[1] = MATRIX_A;
33+
}
434

535
@Override
636
public double nextDouble() {
@@ -9,8 +39,27 @@ public double nextDouble() {
939

1040
@Override
1141
public int nextInt() {
12-
// TODO Auto-generated method stub
13-
return 0;
42+
int ret = 0;
43+
if (mti >= N) {
44+
int kk = 0;
45+
for (kk = 0; kk < N-M; ++kk) {
46+
ret = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
47+
mt[kk] = mt[kk+M] ^ (ret >>> 1) ^ mag01[ret & 0x1];
48+
}
49+
for (; kk < N-1; ++kk) {
50+
ret = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK);
51+
mt[kk] = mt[kk+(M-N)] ^ (ret >>> 1) ^ mag01[ret & 0x1];
52+
}
53+
ret = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK);
54+
mt[N-1] = mt[M-1] ^ (ret >>> 1) ^ mag01[ret & 0x1];
55+
mti = 0;
56+
}
57+
ret = mt[mti++];
58+
ret ^= (ret >>> 11);
59+
ret ^= (ret << 7) & TEMPERING_MASK_B;
60+
ret ^= (ret << 15) & TEMPERING_MASK_C;
61+
ret ^= (ret >>> 18);
62+
return ret;
1463
}
1564

1665
@Override

src/main/java/org/thejavaguy/prng/generators/R250_521.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public R250_521() {
2828
public R250_521(long seed) {
2929
r250_buffer = new int[NUM_ELEMENTS_250];
3030
r521_buffer = new int[NUM_ELEMENTS_521];
31-
Random r = new Random();
31+
Random r = new Random(seed);
3232
int i = NUM_ELEMENTS_521;
3333
while (i-- > 250) {
3434
r521_buffer[i] = r.nextInt();
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
package org.thejavaguy.prng.generators;
2+
3+
import static org.junit.Assert.*;
4+
import java.util.Set;
5+
import java.util.TreeSet;
6+
import org.junit.After;
7+
import org.junit.Before;
8+
import org.junit.Test;
9+
10+
public class MersenneTwisterTest {
11+
private MersenneTwister sut;
12+
13+
@Before
14+
public void setUp() throws Exception {
15+
sut = new MersenneTwister(645564);
16+
}
17+
18+
@After
19+
public void tearDown() throws Exception {
20+
sut = null;
21+
}
22+
23+
@Test
24+
public void nextDouble_IsAlwaysGEQ0andL1() {
25+
int reps = 10_000_000;
26+
for (int i = 0; i < reps; ++i) {
27+
double actual = sut.nextDouble();
28+
assertTrue(actual >= 0);
29+
assertTrue(actual < 1);
30+
}
31+
}
32+
33+
@Test
34+
public void nextInt_WithUpperLimit_IsAlwaysWithinLimit() {
35+
int reps = 10_000_000;
36+
Set<Integer> results = new TreeSet<>();
37+
for (int i = 0; i < reps; ++i) {
38+
int actual = sut.nextInt(10);
39+
results.add(actual);
40+
assertTrue(actual >= 0);
41+
assertTrue(actual <= 10);
42+
}
43+
assertEquals(11, results.size());
44+
}
45+
46+
@Test
47+
public void nextInt_WithUpperLimit_IsAlwaysWithinLimit2() {
48+
int reps = 10_000_000;
49+
for (int i = 0; i < reps; ++i) {
50+
int actual = sut.nextInt(1);
51+
assertTrue(actual >= 0);
52+
assertTrue(actual <= 1);
53+
}
54+
}
55+
56+
@Test
57+
public void nextInt_WithNegativeLimits_IsAlwaysWithinLimits() {
58+
int reps = 10_000_000;
59+
Set<Integer> results = new TreeSet<>();
60+
for (int i = 0; i < reps; ++i) {
61+
int actual = sut.nextInt(-10, -1);
62+
results.add(Integer.valueOf(actual));
63+
assertTrue(actual >= -10);
64+
assertTrue(actual <= -1);
65+
}
66+
assertEquals(10, results.size());
67+
}
68+
69+
@Test
70+
public void nextByte_IsAlwaysWithinLimits() {
71+
int reps = 10_000_000;
72+
Set<Integer> results = new TreeSet<>();
73+
for (int i = 0; i < reps; ++i) {
74+
byte actual = sut.nextByte();
75+
results.add(Integer.valueOf(actual));
76+
assertTrue(actual >= -128);
77+
assertTrue(actual <= 127);
78+
}
79+
assertEquals(256, results.size());
80+
}
81+
}

0 commit comments

Comments
 (0)