Skip to content

Commit

Permalink
Add inverseCumulativeProbability to discrete uniform distribution
Browse files Browse the repository at this point in the history
  • Loading branch information
aherbert committed Oct 6, 2021
1 parent 312f0ab commit 55e3ef9
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,21 @@ public double survivalProbability(int x) {
return ((double) upper - x) / upperMinusLowerPlus1;
}

/** {@inheritDoc} */
@Override
public int inverseCumulativeProbability(final double p) {
ArgumentUtils.checkProbability(p);
// Casting will clip overflows to int min or max value
final int x = (int) (Math.ceil(p * upperMinusLowerPlus1 + lower - 1));
// Note: x may be too high due to floating-point error and rounding up with ceil.
// Return the next value down if that is also above the input cumulative probability.
// This ensures x == icdf(cdf(x))
if (x <= lower) {
return lower;
}
return cumulativeProbability(x - 1) >= p ? x - 1 : x;
}

/**
* {@inheritDoc}
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,4 +98,36 @@ void testLargeRangeAdditionOverflow() {

Assertions.assertEquals(hi, dist.getMean());
}

/**
* Test the inverse CDF returns the correct x from the CDF result.
* This case was identified using various x and upper bounds to discover a mismatch
* of x != icdf(cdf(x)). This occurs due to rounding errors on the inversion.
*/
@Test
void testInverseCDF() {
final int lower = 3;
final int x = 23;
final int upper = 40;
final double range = (double) upper - lower + 1;

final UniformDiscreteDistribution dist = new UniformDiscreteDistribution(lower, upper);
// Compute p and check it is as expected
final double p = dist.cumulativeProbability(x);
Assertions.assertEquals((x - lower + 1) / range, p);

// Invert
final double value = Math.ceil(p * range + lower - 1);
Assertions.assertEquals(x + 1, value, "Expected a rounding error");

Assertions.assertEquals(x, dist.inverseCumulativeProbability(p), "Expected rounding correction");

// Test for overflow of an integer when inverting
final int min = Integer.MIN_VALUE;
final UniformDiscreteDistribution dist2 = new UniformDiscreteDistribution(min, min + 10);
Assertions.assertEquals(min, dist2.inverseCumulativeProbability(0.0));
final int max = Integer.MAX_VALUE;
final UniformDiscreteDistribution dist3 = new UniformDiscreteDistribution(max - 10, max);
Assertions.assertEquals(max, dist3.inverseCumulativeProbability(1.0));
}
}

0 comments on commit 55e3ef9

Please sign in to comment.