Fixed Rngs
- removed rngs - added method for streams in Rng - changed the number of streams possibles - parallel and iterative run give now the same results
This commit is contained in:
@@ -1,47 +1,51 @@
|
||||
package net.berack.upo.valpre.rand;
|
||||
|
||||
/**
|
||||
* This is an Java library for random number generation. The use of this
|
||||
* This class has been modified by Giacomo Bertolazzi in a way that doesn`t
|
||||
* resemble the original. It still has the same role, but has been extended and
|
||||
* modernized to java 23.
|
||||
*
|
||||
* This is an Java library for random number generation. The use of this
|
||||
* library is recommended as a replacement for the Java class Random,
|
||||
* particularly in simulation applications where the statistical
|
||||
* 'goodness' of the random number generator is important.
|
||||
*
|
||||
* The generator used in this library is a so-called 'Lehmer random number
|
||||
* generator' which returns a pseudo-random number uniformly distributed
|
||||
* between 0.0 and 1.0. The period is (m - 1) where m = 2,147,483,647 and
|
||||
* between 0.0 and 1.0. The period is (m - 1) where m = 2,147,483,647 and
|
||||
* the smallest and largest possible values are (1 / m) and 1 - (1 / m)
|
||||
* respectively. For more details see:
|
||||
*
|
||||
* "Random Number Generators: Good Ones Are Hard To Find"
|
||||
* Steve Park and Keith Miller
|
||||
* Communications of the ACM, October 1988
|
||||
*
|
||||
* Note that as of 7-11-90 the multiplier used in this library has changed
|
||||
* from the previous "minimal standard" 16807 to a new value of 48271. To
|
||||
* use this library in its old (16807) form change the constants MULTIPLIER
|
||||
* and CHECK as indicated in the comments.
|
||||
*
|
||||
* Name : Rng.java (Random Number Generation - Single Stream)
|
||||
* Authors : Steve Park & Dave Geyer
|
||||
* Translated by : Jun Wang & Richard Dutton
|
||||
* Language : Java
|
||||
* Latest Revision : 6-10-04
|
||||
*
|
||||
* Program rng : Section 2.2
|
||||
* respectively. For more details see:
|
||||
* "Random Number Generators: Good Ones Are Hard To Find"
|
||||
*/
|
||||
public class Rng {
|
||||
public final static long CHECK = 399268537L; /* use 1043616065 for the "minimal standard" */
|
||||
public final static long DEFAULT = 123456789L; /* initial seed, use 0 < DEFAULT < MODULUS */
|
||||
public final static long MODULUS = 2147483647; /* DON'T CHANGE THIS VALUE */
|
||||
public final static long MULTIPLIER = 48271; /* use 16807 for the "minimal standard" */
|
||||
// Streams multipliers values taken from the table at page 114 of
|
||||
// L.M. Leemis, S.K. Park «Discrete event simulation: a first course»
|
||||
public static final long MULT_128 = 40509L;
|
||||
public static final long MULT_256 = 22925L;
|
||||
public static final long MULT_512 = 44857L;
|
||||
public static final long MULT_1024 = 97070L;
|
||||
|
||||
// Single Rng values
|
||||
public final static long DEFAULT = 123456789L;
|
||||
public final static long MODULUS = 2147483647;
|
||||
public final static long MULTIPLIER = 48271;
|
||||
|
||||
private long seed = DEFAULT; /* seed is the state of the generator */
|
||||
|
||||
/**
|
||||
* Default constructor, build the RNG with the default seed. {@link #DEFAULT}
|
||||
*/
|
||||
public Rng() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builde the RNG with a seed passed as paraemter, if negative or zero will be
|
||||
* modified to a positive number by getting the system time.
|
||||
*
|
||||
* @param seed the seed to start the rng
|
||||
*/
|
||||
public Rng(long seed) {
|
||||
this.putSeed(seed);
|
||||
this.setSeed(seed);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -61,35 +65,66 @@ public class Rng {
|
||||
* if x > 0 then x is the initial seed (unless too large)
|
||||
* if x <= 0 then the initial seed is obtained from the system clock
|
||||
*/
|
||||
public void putSeed(long x) {
|
||||
if (x > 0L) {
|
||||
x = x % MODULUS; /* correct if x is too large */
|
||||
} else {
|
||||
x = System.currentTimeMillis();
|
||||
// x = ((unsigned long) time((time_t *) NULL)) % MODULUS;
|
||||
public void setSeed(long seed) {
|
||||
if (seed <= 0L) {
|
||||
seed = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
this.seed = x;
|
||||
this.seed = seed % MODULUS; /* correct if x is too large */
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this (optional) procedure to get the current state of the random
|
||||
* number generator.
|
||||
* Use this procedure to get the current state of the random number generator.
|
||||
*/
|
||||
public long getSeed() {
|
||||
return this.seed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this (optional) procedure to test for a correct implementation.
|
||||
* Get multiple streams for the generation of random numbers. The streams
|
||||
* generated will have the seeds spaced enough that the sequences will not
|
||||
* overlap (if not after many calls)
|
||||
* Note that for efficiency the total number of streams cannot suprass 1024 and
|
||||
* will be casted to a pow of 2 no less than 128 giving the array only 4
|
||||
* possible lengths: 128, 256, 512, 1024
|
||||
*
|
||||
* @param seed the initial seed of the rngs
|
||||
* @param total the total number of streams
|
||||
* @return the streams
|
||||
*/
|
||||
public static boolean testRandom() {
|
||||
Rng rng = new Rng(1); /* set initial state to 1 */
|
||||
for (var i = 0; i < 10000; i++)
|
||||
rng.random();
|
||||
return rng.getSeed() == CHECK;
|
||||
public static Rng[] getMultipleStreams(long seed, int total) {
|
||||
if (total > 1024)
|
||||
throw new IllegalArgumentException("Cannot genrate more than 1024 streams");
|
||||
|
||||
// rounding to the highest pow2
|
||||
total = Math.max(total, 128);
|
||||
total = 1 << (32 - Integer.numberOfLeadingZeros(total - 1));
|
||||
var mult = switch (total) {
|
||||
case 128 -> MULT_128;
|
||||
case 256 -> MULT_256;
|
||||
case 512 -> MULT_512;
|
||||
default -> MULT_1024;
|
||||
};
|
||||
|
||||
// Building the streams
|
||||
var streams = new Rng[total];
|
||||
for (int i = 0; i < total; i++) {
|
||||
streams[i] = new Rng(seed);
|
||||
seed = (seed * mult) % MODULUS;
|
||||
}
|
||||
return streams;
|
||||
}
|
||||
|
||||
/**
|
||||
* This procedure is used for calculating a new seed starting from the one
|
||||
* passed as input. The modulus and multiplier should be passed as well but it
|
||||
* is advised to use the standard ones {@link #MODULUS} and {@link #MULTIPLIER}
|
||||
*
|
||||
* @param modulus the modulus used for the generation
|
||||
* @param multiplier the multiplier used for the generation
|
||||
* @param seed the seed where to start
|
||||
* @return a new seed used for the calculation
|
||||
*/
|
||||
public static long newSeed(long modulus, long multiplier, long seed) {
|
||||
var Q = modulus / multiplier;
|
||||
var R = modulus % multiplier;
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
package net.berack.upo.valpre.rand;
|
||||
|
||||
/**
|
||||
* This is an Java library for multi-stream random number generation.
|
||||
* The use of this library is recommended as a replacement for the Java
|
||||
* class Random, particularly in simulation applications where the
|
||||
* statistical 'goodness' of the random number generator is important.
|
||||
* The library supplies 256 streams of random numbers; use
|
||||
* selectStream(s) to switch between streams indexed s = 0,1,...,255.
|
||||
*
|
||||
* The streams must be initialized. The recommended way to do this is by
|
||||
* using the function plantSeeds(x) with the value of x used to initialize
|
||||
* the default stream and all other streams initialized automatically with
|
||||
* values dependent on the value of x. The following convention is used
|
||||
* to initialize the default stream:
|
||||
* if x > 0 then x is the state
|
||||
* if x < 0 then the state is obtained from the system clock
|
||||
* if x = 0 then the state is to be supplied interactively.
|
||||
*
|
||||
* The generator used in this library is a so-called 'Lehmer random number
|
||||
* generator' which returns a pseudo-random number uniformly distributed
|
||||
* 0.0 and 1.0. The period is (m - 1) where m = 2,147,483,647 and the
|
||||
* smallest and largest possible values are (1 / m) and 1 - (1 / m)
|
||||
* respectively. For more details see:
|
||||
*
|
||||
* "Random Number Generators: Good Ones Are Hard To Find"
|
||||
* Steve Park and Keith Miller
|
||||
* Communications of the ACM, October 1988
|
||||
*
|
||||
* Name : Rngs.java (Random Number Generation - Multiple Streams)
|
||||
* Authors : Steve Park & Dave Geyer
|
||||
* Translated by : Jun Wang & Richard Dutton
|
||||
* Language : Java
|
||||
* Latest Revision : 6-10-04
|
||||
*/
|
||||
public class Rngs {
|
||||
private final static int STREAMS = 256; /* # of streams, DON'T CHANGE THIS VALUE */
|
||||
private final static long A256 = 22925; /* jump multiplier, DON'T CHANGE THIS VALUE */
|
||||
|
||||
private Rng[] rngs;
|
||||
private int current = 0;
|
||||
|
||||
public Rngs(long seed) {
|
||||
this.rngs = new Rng[STREAMS];
|
||||
this.plantSeeds(seed);
|
||||
}
|
||||
|
||||
public Rng getRng() {
|
||||
return this.rngs[this.current];
|
||||
}
|
||||
|
||||
public Rng getRng(int stream) {
|
||||
return this.rngs[stream % STREAMS];
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this function to set the state of all the random number generator
|
||||
* streams by "planting" a sequence of states (seeds), one per stream,
|
||||
* with all states dictated by the state of the default stream.
|
||||
* The sequence of planted states is separated one from the next by
|
||||
* 8,367,782 calls to Random().
|
||||
*/
|
||||
public void plantSeeds(long seed0) {
|
||||
this.rngs[0] = new Rng(seed0);
|
||||
|
||||
for (int j = 1; j < STREAMS; j++) {
|
||||
seed0 = Rng.newSeed(Rng.MODULUS, A256, seed0);
|
||||
this.rngs[j] = new Rng(seed0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this function to set the current random number generator
|
||||
* stream -- that stream from which the next random number will come.
|
||||
*/
|
||||
public void selectStream(int index) {
|
||||
this.current = index % STREAMS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this (optional) function to test for a correct implementation.
|
||||
*/
|
||||
public static boolean testRandom() {
|
||||
var rngs = new Rngs(1);
|
||||
var first = rngs.getRng();
|
||||
for (int i = 0; i < 10000; i++)
|
||||
first.random();
|
||||
|
||||
var x = first.getSeed(); /* get the new state value */
|
||||
var ok = (x == Rng.CHECK); /* and check for correctness */
|
||||
|
||||
x = rngs.getRng(1).getSeed(); /* get the state of stream 1 */
|
||||
return ok && (x == A256); /* x should be the jump multiplier */
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import net.berack.upo.valpre.rand.Rng;
|
||||
import net.berack.upo.valpre.rand.Rngs;
|
||||
import net.berack.upo.valpre.sim.stats.ResultMultiple;
|
||||
import net.berack.upo.valpre.sim.stats.Result;
|
||||
|
||||
@@ -33,11 +32,11 @@ public class SimulationMultiple {
|
||||
* @return The statistics the network.
|
||||
*/
|
||||
public ResultMultiple run(long seed, int runs, EndCriteria... criterias) {
|
||||
var rng = new Rng(seed);
|
||||
var rngs = Rng.getMultipleStreams(seed, runs);
|
||||
var stats = new Result[runs];
|
||||
|
||||
for (int i = 0; i < runs; i++) {
|
||||
var sim = new Simulation(this.net, rng, criterias);
|
||||
var sim = new Simulation(this.net, rngs[i], criterias);
|
||||
stats[i] = sim.run();
|
||||
}
|
||||
return new ResultMultiple(stats);
|
||||
@@ -60,7 +59,7 @@ public class SimulationMultiple {
|
||||
*/
|
||||
public ResultMultiple runParallel(long seed, int runs, EndCriteria... criterias)
|
||||
throws InterruptedException, ExecutionException {
|
||||
var rngs = new Rngs(seed);
|
||||
var rngs = Rng.getMultipleStreams(seed, runs);
|
||||
var results = new Result[runs];
|
||||
var futures = new Future[runs];
|
||||
|
||||
@@ -69,7 +68,7 @@ public class SimulationMultiple {
|
||||
for (int i = 0; i < runs; i++) {
|
||||
final var id = i;
|
||||
futures[i] = threads.submit(() -> {
|
||||
var sim = new Simulation(this.net, rngs.getRng(id), criterias);
|
||||
var sim = new Simulation(this.net, rngs[id], criterias);
|
||||
results[id] = sim.run();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package net.berack.upo.valpre.rand;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -7,8 +9,35 @@ import java.util.Arrays;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class TestRandom {
|
||||
|
||||
@Test
|
||||
public void testRng() {
|
||||
Rng rng = new Rng(1);
|
||||
for (var i = 0; i < 10000; i++)
|
||||
rng.random();
|
||||
assertEquals(399268537L, rng.getSeed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRngs() {
|
||||
var rngs = Rng.getMultipleStreams(1, 200);
|
||||
assertEquals(256, rngs.length);
|
||||
|
||||
var rng0 = rngs[0];
|
||||
var rng1 = rngs[1];
|
||||
|
||||
for (int i = 0; i < 8367781; i++) {
|
||||
rng0.random();
|
||||
assertNotEquals(rng1.getSeed(), rng0.getSeed());
|
||||
}
|
||||
assertEquals(Rng.MULT_256, rng1.getSeed());
|
||||
|
||||
rng0.random();
|
||||
assertEquals(rng0.getSeed(), rng1.getSeed());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRngVariance() {
|
||||
var numbers = new int[5000];
|
||||
var rng = new Rng(4656);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user