Skip to content

Commit 334088f

Browse files
committed
more experimental random number generators
1 parent d64ef7b commit 334088f

File tree

6 files changed

+167
-2
lines changed

6 files changed

+167
-2
lines changed

src/main/java/de/tilman_neumann/jml/base/Uint128.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,32 @@ public static Uint128 mul128_getLow(Uint128 a, Uint128 b) { // derived from mul1
433433
return r_lo;
434434
}
435435

436+
/**
437+
* Get the lower 128 bit integer of a multiplication of two unsigned 128 bit integers, using Math.multiplyHigh().
438+
*
439+
* @param a Uint128
440+
* @param b Uint128
441+
* @return the low Uint128 of a*b
442+
*/
443+
public static Uint128 mul128MH_getLow(Uint128 a, Uint128 b) { // derived from mul128_v2
444+
final long a_hi = a.getHigh(); // a >>> 32;
445+
final long b_hi = b.getHigh(); // b >>> 32;
446+
final long a_lo = a.getLow(); // a & 0xFFFFFFFFL;
447+
final long b_lo = b.getLow(); // b & 0xFFFFFFFFL;
448+
449+
final Uint128 lo_prod = mul64_MH(a_lo, b_lo); // a_lo * b_lo;
450+
final Uint128 med_prod1 = mul64_MH(a_hi, b_lo); // a_hi * b_lo;
451+
final Uint128 med_prod2 = mul64_MH(a_lo, b_hi); // a_lo * b_hi;
452+
final Uint128 med_term = med_prod1.add(med_prod2); // med_prod1 + med_prod2;
453+
454+
//final long r_lo = ((med_term & 0xFFFFFFFFL) << 32) + lo_prod;
455+
final long med_term_lo = med_term.getLow(); // (med_term & 0xFFFFFFFFL)
456+
final Uint128 r_lo = new Uint128(med_term_lo, 0).add(lo_prod);
457+
458+
//return new Uint128(r_hi, r_lo);
459+
return r_lo;
460+
}
461+
436462
/**
437463
* Compute quotient and remainder of this / v.
438464
* The quotient will be correct only if it is <= 64 bit.

src/main/java/de/tilman_neumann/jml/random/LehmerRng64.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
/**
1919
* Lehmer's random number generator for 64 bit numbers; requires 128 bit multiplication internally.
2020
*
21-
* This is class is still experimental. It seems to work but not much faster than Random yet.
21+
* This is class is still <strong>experimental</strong>. It seems to work but not much faster than Random yet.
2222
*
2323
* @see https://en.wikipedia.org/wiki/Lehmer_random_number_generator
2424
*/
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
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 de.tilman_neumann.jml.base.Uint128;
17+
18+
/**
19+
* Lehmer's random number generator for 64 bit numbers, using Math.multiplyHigh(); requires 128 bit multiplication internally.
20+
*
21+
* This is class is still <strong>experimental</strong>.
22+
*
23+
* @see https://en.wikipedia.org/wiki/Lehmer_random_number_generator
24+
*/
25+
public class LehmerRng64MH {
26+
private static final Uint128 mult = new Uint128(0x12e15e35b500f16eL, 0x2e714eb2b37916a5L);
27+
28+
private Uint128 state = mult;
29+
30+
public long nextLong() {
31+
long result = state.getHigh();
32+
state = Uint128.mul128MH_getLow(state, mult);
33+
return result;
34+
}
35+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
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+
* An <strong>experimental</strong> variant of Marsaglia's xorshf generator.
18+
*
19+
* @author Tilman Neumann
20+
*/
21+
public class Xorshf32b {
22+
23+
private int x=123456789, y=362436069, z=521288629;
24+
25+
public int nextInt() {
26+
int t;
27+
x ^= x << 16;
28+
x ^= x >> 5;
29+
x ^= x << 1;
30+
31+
t = x;
32+
x = y;
33+
y = z;
34+
z = t ^ x ^ y;
35+
36+
return z;
37+
}
38+
39+
public int nextInt(int max) {
40+
final long l = nextInt(); // up to unsigned 2^32 - 1
41+
final long prod = l * max; // up to max * (2^32 - 1)
42+
return (int) (prod >>> 32);
43+
}
44+
45+
public int nextInt(int min, int max) {
46+
return min + nextInt(max - min);
47+
}
48+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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+
* <strong>Experimental</strong> 64 bit random number generator computing longs from two consecutive 32 bit random numbers.
18+
*
19+
* @author Tilman Neumann
20+
*/
21+
public class Xorshf64 {
22+
23+
private Xorshf32 intGenerator = new Xorshf32();
24+
25+
public long nextLong() {
26+
return ((long) intGenerator.nextInt()) * intGenerator.nextInt();
27+
}
28+
29+
// TODO nextLong(upper)
30+
31+
// TODO nextLong(lower, upper)
32+
33+
}

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ public class RngPerformanceTest {
4343
private static final Rng rng = new Rng();
4444
private static final SpRand32 spRand32 = new SpRand32();
4545
private static final Xorshf32 xorshf32 = new Xorshf32();
46+
private static final Xorshf32b xorshf32b = new Xorshf32b();
4647
private static final LehmerRng64 lehmer64 = new LehmerRng64();
47-
48+
private static final LehmerRng64MH lehmer64MH = new LehmerRng64MH();
49+
private static final Xorshf64 xorshf64 = new Xorshf64();
50+
4851
/**
4952
* Test.
5053
* @param args ignored
@@ -76,6 +79,10 @@ public static void main(String[] args) {
7679
xorshf32.nextInt();
7780
}
7881
LOG.debug("Xorshf32.nextInt() took " + timer.capture() + " ms");
82+
for (int i=0; i<NCOUNT; i++) {
83+
xorshf32b.nextInt();
84+
}
85+
LOG.debug("Xorshf32b.nextInt() took " + timer.capture() + " ms");
7986

8087
// test nextInt(int upper)
8188
int upper = 1<<30 + 29;
@@ -101,6 +108,10 @@ public static void main(String[] args) {
101108
xorshf32.nextInt(upper);
102109
}
103110
LOG.debug("Xorshf32.nextInt(upper) took " + timer.capture() + " ms");
111+
for (int i=0; i<NCOUNT; i++) {
112+
xorshf32b.nextInt(upper);
113+
}
114+
LOG.debug("Xorshf32b.nextInt(upper) took " + timer.capture() + " ms");
104115

105116
// test nextInt(int lower, int upper)
106117
int lower = 12345;
@@ -128,6 +139,10 @@ public static void main(String[] args) {
128139
xorshf32.nextInt(lower, upper);
129140
}
130141
LOG.debug("Xorshf32.nextInt(lower, upper) took " + timer.capture() + " ms");
142+
for (int i=0; i<NCOUNT; i++) {
143+
xorshf32b.nextInt(lower, upper);
144+
}
145+
LOG.debug("Xorshf32b.nextInt(lower, upper) took " + timer.capture() + " ms");
131146

132147
// test nextLong()
133148
if (TEST_SLOW) {
@@ -148,5 +163,13 @@ public static void main(String[] args) {
148163
lehmer64.nextLong();
149164
}
150165
LOG.debug("LehmerRng64.nextLong() took " + timer.capture() + " ms");
166+
for (int i=0; i<NCOUNT; i++) {
167+
lehmer64MH.nextLong();
168+
}
169+
LOG.debug("LehmerRng64MH.nextLong() took " + timer.capture() + " ms");
170+
for (int i=0; i<NCOUNT; i++) {
171+
xorshf64.nextLong();
172+
}
173+
LOG.debug("Xorshf64.nextLong() took " + timer.capture() + " ms");
151174
}
152175
}

0 commit comments

Comments
 (0)