diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..e0f15db --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "automatic" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index 15cf713..70a86c3 100644 --- a/pom.xml +++ b/pom.xml @@ -13,4 +13,18 @@ 23 + + + org.junit.jupiter + junit-jupiter-engine + 5.2.0 + test + + + org.junit.platform + junit-platform-runner + 1.2.0 + test + + \ No newline at end of file diff --git a/src/main/java/net/berack/upo/valpre/rand/Distribution.java b/src/main/java/net/berack/upo/valpre/rand/Distribution.java index 82310c3..4d06cda 100644 --- a/src/main/java/net/berack/upo/valpre/rand/Distribution.java +++ b/src/main/java/net/berack/upo/valpre/rand/Distribution.java @@ -10,7 +10,7 @@ public interface Distribution { * Represents an exponential distribution. */ public static class Exponential implements Distribution { - private final double lambda; + public final double lambda; /** * Creates a new exponential distribution with the given rate. @@ -31,8 +31,8 @@ public interface Distribution { * Represents a normal distribution. */ public static class Normal implements Distribution { - private final double mean; - private final double sigma; + public final double mean; + public final double sigma; /** * Creates a new normal distribution with the given mean and standard deviation. @@ -56,8 +56,8 @@ public interface Distribution { * Represents a normal distribution using the Box-Muller transform. */ public static class NormalBoxMuller implements Distribution { - private final double mean; - private final double sigma; + public final double mean; + public final double sigma; /** * Creates a new normal distribution with the given mean and standard deviation. @@ -74,9 +74,102 @@ public interface Distribution { public double sample(Rng rng) { var sample1 = rng.random(); var sample2 = rng.random(); - //remove the other value for thread safety - //next = mean + sigma * Math.sqrt(-2 * Math.log(sample1)) * Math.sin(2 * Math.PI * sample2); + // remove the other value for thread safety + // next = mean + sigma * Math.sqrt(-2 * Math.log(sample1)) * Math.sin(2 * + // Math.PI * sample2); return mean + sigma * Math.sqrt(-2 * Math.log(sample1)) * Math.cos(2 * Math.PI * sample2); } } + + /** + * Represent a uniform distribution. + */ + public static class Uniform implements Distribution { + public final double min; + public final double max; + + /** + * Creates a new uniform distribution with the given min value and max value. + * + * @param min the minimum value possible + * @param max the maximum value possible + */ + public Uniform(double min, double max) { + this.min = min; + this.max = max; + } + + @Override + public double sample(Rng rng) { + return min + rng.random() * (max - min); + } + } + + /** + * Represent an Erlang distribution. + */ + public static class Erlang implements Distribution { + public final int k; + public final double lambda; + + /** + * Creates a new erlang distribution with the given K exponentials, all with the + * same lambda. + * + * @param k the number of exponentials + * @param lambda the lambda of the exponentials + */ + public Erlang(int k, double lambda) { + this.k = k; + this.lambda = lambda; + } + + @Override + public double sample(Rng rng) { + var product = 1.0; + for (int i = 0; i < this.k; i++) { + product *= rng.random(); + } + return -Math.log(product) / this.lambda; + } + } + + /** + * Represent a HyperExponential distribution. + */ + public static class HyperExponential implements Distribution { + private final double[] lambdas; + private final double[] probabilities; + + /** + * Creates a new hyperexponential distribution with the given lambdas and their + * corresponding probabilities. + * + * @param lambdas the array of lambda values for the exponential + * distributions + * @param probabilities the array of probabilities for each lambda + */ + public HyperExponential(double[] lambdas, double[] probabilities) { + if (lambdas.length != probabilities.length) { + throw new IllegalArgumentException("Lambdas and probabilities must have the same length"); + } + this.lambdas = lambdas; + this.probabilities = probabilities; + } + + @Override + public double sample(Rng rng) { + var randomValue = rng.random(); + var i = 0; + + while (i < probabilities.length) { + randomValue -= probabilities[i]; + if (randomValue <= 0.0d) + break; + i += 1; + } + + return -Math.log(rng.random()) / lambdas[i]; + } + } } diff --git a/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java b/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java index 34bde62..b800c81 100644 --- a/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java +++ b/src/main/java/net/berack/upo/valpre/sim/stats/Statistics.java @@ -88,7 +88,7 @@ public class Statistics { } /** - * TODO + * * * @param save * @param val1 diff --git a/src/test/java/net/berack/upo/valpre/rand/TestRandom.java b/src/test/java/net/berack/upo/valpre/rand/TestRandom.java new file mode 100644 index 0000000..3d5449c --- /dev/null +++ b/src/test/java/net/berack/upo/valpre/rand/TestRandom.java @@ -0,0 +1,29 @@ +package net.berack.upo.valpre.rand; + +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.jupiter.api.Test; + +public class TestRandom { + @Test + public void testRng() { + var numbers = new int[5000]; + var rng = new Rng(4656); + + for (var i = 0; i < 1000000; i++) { + var sample = rng.random(); + var index = (int) (sample * numbers.length); + numbers[index] += 1; + } + + var avg = (double) Arrays.stream(numbers).sum() / numbers.length; + var variance = Arrays.stream(numbers).mapToDouble(num -> Math.pow(num - avg, 2)).sum() / numbers.length; + var stdDev = Math.sqrt(variance); + var expected = Math.sqrt((double) numbers.length / 12); + expected *= 1.1; // adding a bit of margin + + assertTrue("Standard Dev must be less than [" + expected + "] -> [" + stdDev + "]", stdDev < expected); + } +}