mirror of
https://github.com/GTNewHorizons/GTNHLib.git
synced 2026-11-08 12:34:52 +00:00
Move StdLCG to GTNHLib (#304)
This commit is contained in:
1 parent
2751220f03
commit
bc518d841d
2 files changed
+112
No files matched your search
@@ -0,0 +1,65 @@
|
||||
package com.gtnewhorizon.gtnhlib.util;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
/// A basic LCG, based on Knuth's work and using the same constants as {@link Random}. As such, it produces the same
|
||||
/// output, just MUCH faster since it's not reliant on atomic ops. However, each instance can only be used by one thread
|
||||
/// at a time.
|
||||
public class StdLCG extends Random {
|
||||
|
||||
protected static final long mask = -1L >>> 16;
|
||||
protected static final long multiplier = 0x5DEECE66DL;
|
||||
protected static final long increment = 11;
|
||||
protected long seed;
|
||||
protected boolean haveNextNextGaussian = false;
|
||||
protected double nextNextGaussian = 0.0;
|
||||
|
||||
/// Don't ask me why stdlib scrambles the seed, because I don't know.
|
||||
public StdLCG(long seed) {
|
||||
// this default constructor performs an atomic op on a global variable
|
||||
super(seed);
|
||||
}
|
||||
|
||||
public StdLCG() {
|
||||
this(System.nanoTime());
|
||||
}
|
||||
|
||||
public static long scramble(long seed) {
|
||||
return (seed ^ multiplier) & mask;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSeed(long seed) {
|
||||
this.seed = scramble(seed);
|
||||
haveNextNextGaussian = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int next(int bits) {
|
||||
seed = (seed * multiplier + increment) & mask;
|
||||
return (int) (seed >>> 48 - bits);
|
||||
}
|
||||
|
||||
/// A reimplementation of Knuth's algorithm, except I read the 2nd edition instead of the 3rd. It's probably close
|
||||
/// enough.
|
||||
@Override
|
||||
public double nextGaussian() {
|
||||
if (haveNextNextGaussian) {
|
||||
haveNextNextGaussian = false;
|
||||
return nextNextGaussian;
|
||||
}
|
||||
|
||||
double s, v1, v2;
|
||||
do {
|
||||
v1 = 2 * nextDouble() - 1;
|
||||
v2 = 2 * nextDouble() - 1;
|
||||
s = v1 * v1 + v2 * v2;
|
||||
// Knuth doesn't say to return if s==0, but stdlib does. We need to match stdlib, so another clause it is.
|
||||
} while (s >= 1 || s == 0);
|
||||
|
||||
final var tmpRoot = StrictMath.sqrt(-2 * StrictMath.log(s) / s);
|
||||
nextNextGaussian = v2 * tmpRoot;
|
||||
haveNextNextGaussian = true;
|
||||
return v1 * tmpRoot;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.gtnewhorizon.gtnhlib.test.util;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.gtnewhorizon.gtnhlib.util.StdLCG;
|
||||
|
||||
/// Ensures {@link StdLCG} matches {@link Random}'s output in a variety of scenarios.
|
||||
public class StdLCGTest {
|
||||
|
||||
@Test
|
||||
void testNextLongConformance() {
|
||||
// ...after construction,
|
||||
final var stdlib = new Random(900);
|
||||
final var lcg = new StdLCG(900);
|
||||
assertEquals(stdlib.nextLong(), lcg.nextLong());
|
||||
assertEquals(stdlib.nextLong(), lcg.nextLong());
|
||||
assertEquals(stdlib.nextLong(), lcg.nextLong());
|
||||
|
||||
// ...and after post-construction seeding
|
||||
stdlib.setSeed(9425125);
|
||||
lcg.setSeed(9425125);
|
||||
assertEquals(stdlib.nextLong(), lcg.nextLong());
|
||||
assertEquals(stdlib.nextLong(), lcg.nextLong());
|
||||
assertEquals(stdlib.nextLong(), lcg.nextLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testNextGaussianConformance() {
|
||||
// ...after construction,
|
||||
final var stdlib = new Random(900);
|
||||
final var lcg = new StdLCG(900);
|
||||
assertEquals(stdlib.nextGaussian(), lcg.nextGaussian());
|
||||
assertEquals(stdlib.nextGaussian(), lcg.nextGaussian());
|
||||
assertEquals(stdlib.nextGaussian(), lcg.nextGaussian());
|
||||
|
||||
// ...and after post-construction seeding
|
||||
stdlib.setSeed(9425125);
|
||||
lcg.setSeed(9425125);
|
||||
assertEquals(stdlib.nextGaussian(), lcg.nextGaussian());
|
||||
assertEquals(stdlib.nextGaussian(), lcg.nextGaussian());
|
||||
assertEquals(stdlib.nextGaussian(), lcg.nextGaussian());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user