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;
|
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,
|
* library is recommended as a replacement for the Java class Random,
|
||||||
* particularly in simulation applications where the statistical
|
* particularly in simulation applications where the statistical
|
||||||
* 'goodness' of the random number generator is important.
|
* 'goodness' of the random number generator is important.
|
||||||
*
|
*
|
||||||
* The generator used in this library is a so-called 'Lehmer random number
|
* The generator used in this library is a so-called 'Lehmer random number
|
||||||
* generator' which returns a pseudo-random number uniformly distributed
|
* 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)
|
* the smallest and largest possible values are (1 / m) and 1 - (1 / m)
|
||||||
* respectively. For more details see:
|
* respectively. For more details see:
|
||||||
*
|
* "Random Number Generators: Good Ones Are Hard To Find"
|
||||||
* "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
|
|
||||||
*/
|
*/
|
||||||
public class Rng {
|
public class Rng {
|
||||||
public final static long CHECK = 399268537L; /* use 1043616065 for the "minimal standard" */
|
// Streams multipliers values taken from the table at page 114 of
|
||||||
public final static long DEFAULT = 123456789L; /* initial seed, use 0 < DEFAULT < MODULUS */
|
// L.M. Leemis, S.K. Park «Discrete event simulation: a first course»
|
||||||
public final static long MODULUS = 2147483647; /* DON'T CHANGE THIS VALUE */
|
public static final long MULT_128 = 40509L;
|
||||||
public final static long MULTIPLIER = 48271; /* use 16807 for the "minimal standard" */
|
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 */
|
private long seed = DEFAULT; /* seed is the state of the generator */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor, build the RNG with the default seed. {@link #DEFAULT}
|
||||||
|
*/
|
||||||
public Rng() {
|
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) {
|
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 x is the initial seed (unless too large)
|
||||||
* if x <= 0 then the initial seed is obtained from the system clock
|
* if x <= 0 then the initial seed is obtained from the system clock
|
||||||
*/
|
*/
|
||||||
public void putSeed(long x) {
|
public void setSeed(long seed) {
|
||||||
if (x > 0L) {
|
if (seed <= 0L) {
|
||||||
x = x % MODULUS; /* correct if x is too large */
|
seed = System.currentTimeMillis();
|
||||||
} else {
|
|
||||||
x = System.currentTimeMillis();
|
|
||||||
// x = ((unsigned long) time((time_t *) NULL)) % MODULUS;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
* Use this procedure to get the current state of the random number generator.
|
||||||
* number generator.
|
|
||||||
*/
|
*/
|
||||||
public long getSeed() {
|
public long getSeed() {
|
||||||
return this.seed;
|
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() {
|
public static Rng[] getMultipleStreams(long seed, int total) {
|
||||||
Rng rng = new Rng(1); /* set initial state to 1 */
|
if (total > 1024)
|
||||||
for (var i = 0; i < 10000; i++)
|
throw new IllegalArgumentException("Cannot genrate more than 1024 streams");
|
||||||
rng.random();
|
|
||||||
return rng.getSeed() == CHECK;
|
// 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) {
|
public static long newSeed(long modulus, long multiplier, long seed) {
|
||||||
var Q = modulus / multiplier;
|
var Q = modulus / multiplier;
|
||||||
var R = 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 java.util.concurrent.Future;
|
||||||
|
|
||||||
import net.berack.upo.valpre.rand.Rng;
|
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.ResultMultiple;
|
||||||
import net.berack.upo.valpre.sim.stats.Result;
|
import net.berack.upo.valpre.sim.stats.Result;
|
||||||
|
|
||||||
@@ -33,11 +32,11 @@ public class SimulationMultiple {
|
|||||||
* @return The statistics the network.
|
* @return The statistics the network.
|
||||||
*/
|
*/
|
||||||
public ResultMultiple run(long seed, int runs, EndCriteria... criterias) {
|
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];
|
var stats = new Result[runs];
|
||||||
|
|
||||||
for (int i = 0; i < runs; i++) {
|
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();
|
stats[i] = sim.run();
|
||||||
}
|
}
|
||||||
return new ResultMultiple(stats);
|
return new ResultMultiple(stats);
|
||||||
@@ -60,7 +59,7 @@ public class SimulationMultiple {
|
|||||||
*/
|
*/
|
||||||
public ResultMultiple runParallel(long seed, int runs, EndCriteria... criterias)
|
public ResultMultiple runParallel(long seed, int runs, EndCriteria... criterias)
|
||||||
throws InterruptedException, ExecutionException {
|
throws InterruptedException, ExecutionException {
|
||||||
var rngs = new Rngs(seed);
|
var rngs = Rng.getMultipleStreams(seed, runs);
|
||||||
var results = new Result[runs];
|
var results = new Result[runs];
|
||||||
var futures = new Future[runs];
|
var futures = new Future[runs];
|
||||||
|
|
||||||
@@ -69,7 +68,7 @@ public class SimulationMultiple {
|
|||||||
for (int i = 0; i < runs; i++) {
|
for (int i = 0; i < runs; i++) {
|
||||||
final var id = i;
|
final var id = i;
|
||||||
futures[i] = threads.submit(() -> {
|
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();
|
results[id] = sim.run();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package net.berack.upo.valpre.rand;
|
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 static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
@@ -7,8 +9,35 @@ import java.util.Arrays;
|
|||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
public class TestRandom {
|
public class TestRandom {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRng() {
|
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 numbers = new int[5000];
|
||||||
var rng = new Rng(4656);
|
var rng = new Rng(4656);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user