package org.openlca.core.math; import java.util.Random; public abstract class NumberGenerator { public abstract double next(); public static NumberGenerator normal(double mean, double standardDeviation) { return new Normal(mean, standardDeviation); } public static NumberGenerator logNormal(double geometricMean, double geometricStandardDeviation) { return new LogNormal(geometricMean, geometricStandardDeviation); } public static NumberGenerator uniform(double min, double max) { if (min == max) return new Discrete(max); if (max < min) return new Uniform(max, min); return new Uniform(min, max); } public static NumberGenerator triangular(double min, double mode, double max) { return new Triangular(min, mode, max); } public static NumberGenerator discrete(double val) { return new Discrete(val); } private static class Normal extends NumberGenerator { private final Random rand; private final double mean; private final double std; Normal(double mean, double std) { this.mean = mean; this.std = std; this.rand = new Random(); } @Override public double next() { return rand.nextGaussian() * std + mean; } } private static class LogNormal extends NumberGenerator { private final Normal normal; private final double factor; LogNormal(double geoMean, double geoStd) { // the mean and the standard deviation of the *underlying* // distribution is the natural logarithm of the geometric mean and // geometric standard deviation // if the geometric mean is negative (ecoinvent 3!), we generate the // values first for the positive value and multiply them with -1 double gmean; if (geoMean < 0) { gmean = Math.abs(geoMean); factor = -1; } else { gmean = geoMean; factor = 1; } double mean = Math.log(gmean); double std = Math.log(Math.abs(geoStd)); normal = new Normal(mean, std); } @Override public double next() { return Math.exp(normal.next()) * factor; } } private static class Uniform extends NumberGenerator { private final Random rand; private final double min; private final double range; Uniform(double min, double max) { this.min = min; this.range = max - min; this.rand = new Random(); } @Override public double next() { return min + rand.nextDouble() * range; } } private static class Triangular extends NumberGenerator { private double min; private double max; private double mode; Triangular(double min, double mode, double max) { this.min = min; this.mode = mode; this.max = max; } /** * see http://en.wikipedia.org/wiki/Triangular_distribution */ @Override public double next() { if (max == min) return mode; double u = Math.random(); double fMode = (mode - min) / (max - min); if (u <= fMode) return min + Math.sqrt(u * (max - min) * (mode - min)); return max - Math.sqrt((1 - u) * (max - min) * (max - mode)); } } private static class Discrete extends NumberGenerator { private double val; public Discrete(double val) { this.val = val; } @Override public double next() { return val; } } }