diff --git a/src/changes/changes.xml b/src/changes/changes.xml index bf89da1bb..ba579fdaa 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -51,6 +51,11 @@ If the output is not quite correct, check for invisible trailing spaces! + + Calculating the inverse cumulative probability of an "EnumeratedRealDistribution" + will now return the correct result according to the selected enumerated probability + mass function. + Deprecated "ArithmeticUtils#pow(int, long)" and "ArithmeticUtils#pow(long, long)" in favor of corresponding methods "ArithmeticUtils#pow(..., int)". diff --git a/src/main/java/org/apache/commons/math3/distribution/EnumeratedRealDistribution.java b/src/main/java/org/apache/commons/math3/distribution/EnumeratedRealDistribution.java index d4574342e..270764fd4 100644 --- a/src/main/java/org/apache/commons/math3/distribution/EnumeratedRealDistribution.java +++ b/src/main/java/org/apache/commons/math3/distribution/EnumeratedRealDistribution.java @@ -18,11 +18,13 @@ package org.apache.commons.math3.distribution; import java.util.ArrayList; import java.util.List; + import org.apache.commons.math3.exception.DimensionMismatchException; import org.apache.commons.math3.exception.MathArithmeticException; import org.apache.commons.math3.exception.NotANumberException; import org.apache.commons.math3.exception.NotFiniteNumberException; import org.apache.commons.math3.exception.NotPositiveException; +import org.apache.commons.math3.exception.OutOfRangeException; import org.apache.commons.math3.random.RandomGenerator; import org.apache.commons.math3.random.Well19937c; import org.apache.commons.math3.util.Pair; @@ -136,6 +138,33 @@ public class EnumeratedRealDistribution extends AbstractRealDistribution { return probability; } + /** + * {@inheritDoc} + */ + @Override + public double inverseCumulativeProbability(final double p) throws OutOfRangeException { + if (p < 0.0 || p > 1.0) { + throw new OutOfRangeException(p, 0, 1); + } + + double probability = 0; + double x = getSupportLowerBound(); + for (final Pair sample : innerDistribution.getPmf()) { + if (sample.getValue() == 0.0) { + continue; + } + + probability += sample.getValue(); + x = sample.getKey(); + + if (probability >= p) { + break; + } + } + + return x; + } + /** * {@inheritDoc} * diff --git a/src/test/java/org/apache/commons/math3/distribution/EnumeratedRealDistributionTest.java b/src/test/java/org/apache/commons/math3/distribution/EnumeratedRealDistributionTest.java index 182a6c839..6af983563 100644 --- a/src/test/java/org/apache/commons/math3/distribution/EnumeratedRealDistributionTest.java +++ b/src/test/java/org/apache/commons/math3/distribution/EnumeratedRealDistributionTest.java @@ -16,6 +16,8 @@ */ package org.apache.commons.math3.distribution; +import static org.junit.Assert.assertEquals; + import java.util.ArrayList; import java.util.List; @@ -213,4 +215,46 @@ public class EnumeratedRealDistributionTest { Assert.assertEquals(1, new EnumeratedDistribution(list).sample(1).length); } + @Test + public void testIssue1065() { + // Test Distribution for inverseCumulativeProbability + // + // ^ + // | + // 1.000 +--------------------------------o=============== + // | 3| + // | | + // | 1o= + // 0.750 +-------------------------> o== . + // | 3| . . + // | 0 | . . + // 0.5625 +---------------> o==o====== . . + // | | . . . . + // | | . . . . + // | 5| . . . . + // | | . . . . + // | o=== . . . . + // | | . . . . . + // | 4| . . . . . + // | | . . . . . + // 0.000 +=============----+--+------+--+-+---------------> + // 14 18 21 28 31 33 + // + // sum = 4+5+0+3+1+3 = 16 + + EnumeratedRealDistribution distribution = new EnumeratedRealDistribution( + new double[] { 14.0, 18.0, 21.0, 28.0, 31.0, 33.0 }, + new double[] { 4.0 / 16.0, 5.0 / 16.0, 0.0 / 16.0, 3.0 / 16.0, 1.0 / 16.0, 3.0 / 16.0 }); + + assertEquals(14.0, distribution.inverseCumulativeProbability(0.0000), 0.0); + assertEquals(14.0, distribution.inverseCumulativeProbability(0.2500), 0.0); + assertEquals(33.0, distribution.inverseCumulativeProbability(1.0000), 0.0); + + assertEquals(18.0, distribution.inverseCumulativeProbability(0.5000), 0.0); + assertEquals(18.0, distribution.inverseCumulativeProbability(0.5624), 0.0); + assertEquals(28.0, distribution.inverseCumulativeProbability(0.5626), 0.0); + assertEquals(31.0, distribution.inverseCumulativeProbability(0.7600), 0.0); + assertEquals(18.0, distribution.inverseCumulativeProbability(0.5625), 0.0); + assertEquals(28.0, distribution.inverseCumulativeProbability(0.7500), 0.0); + } }