Skip to content

Commit 18c31e2

Browse files
committed
new fast rng for 64 bit numbers
1 parent 5f8f651 commit 18c31e2

File tree

3 files changed

+122
-0
lines changed

3 files changed

+122
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
* java-math-library is a Java library focused on number theory, but not necessarily limited to it. It is based on the PSIQS 4.0 factoring project.
3+
* Copyright (C) 2018-2025 Tilman Neumann - tilman.neumann@web.de
4+
*
5+
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
7+
*
8+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9+
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10+
*
11+
* You should have received a copy of the GNU General Public License along with this program;
12+
* if not, see <http://www.gnu.org/licenses/>.
13+
*/
14+
package de.tilman_neumann.jml.random;
15+
16+
/**
17+
* 64 bit random number generator adapted from https://rosettacode.org/wiki/Pseudo-random_numbers/Splitmix64.
18+
*
19+
* Very fast.
20+
*
21+
* @author Tilman Neumann
22+
*/
23+
final class SplitMix64 {
24+
25+
private static final long constant1 = 0x9e3779b97f4a7c15L;
26+
private static final long constant2 = 0xbf58476d1ce4e5b9L;
27+
private static final long constant3 = 0x94d049bb133111ebL;
28+
29+
private long state;
30+
31+
public SplitMix64() {
32+
state = 90832436910930047L; // just some semiprime, arbitrary choice
33+
}
34+
35+
public SplitMix64(long aSeed) {
36+
state = aSeed;
37+
}
38+
39+
public void seed(long aNumber) {
40+
state = aNumber;
41+
}
42+
43+
public long nextLong() {
44+
long z = (state += constant1);
45+
z = (z ^ (z>>>30)) * constant2;
46+
z = (z ^ (z>>>27)) * constant3;
47+
return z ^ (z>>>31);
48+
}
49+
}

src/test/java/de/tilman_neumann/jml/random/RngPerformanceTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ public class RngPerformanceTest {
5252
private static final LehmerRng64 lehmer64 = new LehmerRng64();
5353
private static final LehmerRng64MH lehmer64MH = new LehmerRng64MH();
5454
private static final Xorshf64 xorshf64 = new Xorshf64();
55+
private static final SplitMix64 splitMix64 = new SplitMix64();
5556

5657
/**
5758
* Test.
@@ -174,6 +175,10 @@ public static void main(String[] args) {
174175
xorshf64.nextLong();
175176
}
176177
LOG.debug("Xorshf64.nextLong() took " + timer.capture() + " ms");
178+
for (int i=0; i<NCOUNT; i++) {
179+
splitMix64.nextLong();
180+
}
181+
LOG.debug("SplitMix64.nextLong() took " + timer.capture() + " ms");
177182

178183
// test nextLong(long upper)
179184
for (int i=0; i<NCOUNT; i++) {
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* java-math-library is a Java library focused on number theory, but not necessarily limited to it. It is based on the PSIQS 4.0 factoring project.
3+
* Copyright (C) 2018-2025 Tilman Neumann - tilman.neumann@web.de
4+
*
5+
* This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License
6+
* as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
7+
*
8+
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
9+
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
10+
*
11+
* You should have received a copy of the GNU General Public License along with this program;
12+
* if not, see <http://www.gnu.org/licenses/>.
13+
*/
14+
package de.tilman_neumann.jml.random;
15+
16+
import static org.junit.Assert.assertEquals;
17+
import static org.junit.Assert.assertTrue;
18+
19+
import java.util.Arrays;
20+
21+
import org.apache.logging.log4j.LogManager;
22+
import org.apache.logging.log4j.Logger;
23+
import org.junit.BeforeClass;
24+
import org.junit.Test;
25+
26+
import de.tilman_neumann.util.ConfigUtil;
27+
28+
public final class SplitMix64Test {
29+
private static final Logger LOG = LogManager.getLogger(SplitMix64Test.class);
30+
31+
private static final int NCOUNT = 1000000;
32+
33+
private static final SplitMix64 rng = new SplitMix64();
34+
35+
@BeforeClass
36+
public static void setup() {
37+
ConfigUtil.initProject();
38+
}
39+
40+
@Test
41+
public void testRosettaExamples() {
42+
SplitMix64 random = new SplitMix64(1234567);
43+
assertEquals(6457827717110365317L, random.nextLong());
44+
assertEquals(3203168211198807973L, random.nextLong());
45+
assertEquals(-8629252141511181193L, random.nextLong()); // 9817491932198370423 - 2^64
46+
assertEquals(4593380528125082431L, random.nextLong());
47+
assertEquals(-2037821214251327795L, random.nextLong()); // 16408922859458223821 - 2^64
48+
}
49+
50+
@Test
51+
public void testNextLongNoArgs() {
52+
long[] firstElements = new long[100];
53+
long min = Long.MAX_VALUE, max = Long.MIN_VALUE;
54+
boolean generatesEven = false, generatesOdd = false;
55+
for (int i=0; i<NCOUNT; i++) {
56+
long n = rng.nextLong();
57+
if (i<100) firstElements[i] = n;
58+
if (n<min) min = n;
59+
if (n>max) max = n;
60+
if ((n & 1) == 1) generatesOdd = true; else generatesEven = true;
61+
}
62+
LOG.debug("First 100 elements: " + Arrays.toString(firstElements));
63+
LOG.debug(NCOUNT + " numbers from " + rng.getClass().getSimpleName() + ".nextLong() gave min = " + min + ", max = " + max);
64+
assertTrue(min < 0);
65+
assertTrue(generatesEven);
66+
assertTrue(generatesOdd);
67+
}
68+
}

0 commit comments

Comments
 (0)