package com.compomics.util.math.statistics.distributions; import com.compomics.util.math.statistics.Distribution; import java.util.HashMap; import java.util.HashSet; import org.apache.commons.math.MathException; import org.apache.commons.math.distribution.BinomialDistributionImpl; import org.apache.commons.math.special.Beta; /** * Implementation of a binomial distribution. * * @author Marc Vaudel */ public class BinomialDistribution implements Distribution { /** * Instance of the apache distribution. */ private BinomialDistributionImpl binomialDistributionImpl; /** * The number of trials. */ private int n; /** * The probability of success of each trial. */ private double p; /** * The maximal number of keys kept in each cache map. */ private int cacheSize = 1000; /** * A cache for the probabilities. */ private HashMap<Integer, Double> pCache = new HashMap<Integer, Double>(); /** * A cache for the cumulative probabilities. */ private HashMap<Integer, Double> descendingCumulativePCache = new HashMap<Integer, Double>(); /** * Constructor. * * @param n the number of trials * @param p the probability of success of each trial */ public BinomialDistribution(int n, double p) { this.n = n; this.p = p; binomialDistributionImpl = new BinomialDistributionImpl(n, p); } @Override public Double getProbabilityAt(double x) { if (x < 0 || x > n) { return 0.0; } int k = (int) x; Double result = pCache.get(k); if (result == null) { result = binomialDistributionImpl.probability(k); addPToCache(k, result); } return result; } /** * Adds a probability to the cache and manages the cache size. * * @param k the value * @param p the probability */ private synchronized void addPToCache(int k, Double p) { if (pCache.size() >= cacheSize) { HashSet<Integer> keys = new HashSet<Integer>(pCache.keySet()); for (Integer key : keys) { pCache.remove(key); if (pCache.size() < cacheSize) { break; } } } pCache.put(k, p); } @Override public Double getCumulativeProbabilityAt(double x) throws MathException { return 1.0 - getDescendingCumulativeProbabilityAt(x); } @Override public Double getDescendingCumulativeProbabilityAt(double x) throws MathException { int k = (int) x; if (k > n) { return 0.0; } else if (k < 0) { return 1.0; } Double result = pCache.get(k); if (result == null) { // adapted from http://commons.apache.org/proper/commons-math/apidocs/src-html/org/apache/commons/math3/distribution/BinomialDistribution.html#line.130 result = Beta.regularizedBeta(p, x + 1.0, n - x); addDescendingCumulativePToCache(k, result); } return result; } /** * Adds a probability to the cache and manages the cache size. * * @param k the value * @param p the probability */ private synchronized void addDescendingCumulativePToCache(int k, Double p) { if (descendingCumulativePCache.size() >= cacheSize) { HashSet<Integer> keys = new HashSet<Integer>(descendingCumulativePCache.keySet()); for (Integer key : keys) { descendingCumulativePCache.remove(key); if (descendingCumulativePCache.size() < cacheSize) { break; } } } descendingCumulativePCache.put(k, p); } /** * Indicates whether all caches are empty. * * @return a boolean indicating whether all caches are empty */ public boolean isCacheEmpty() { return pCache.isEmpty() && descendingCumulativePCache.isEmpty(); } @Override public Double getSmallestCumulativeProbabilityAt(double x) throws MathException { throw new UnsupportedOperationException("Not supported yet."); } @Override public Double getMaxValueForProbability(double p) { throw new UnsupportedOperationException("Not supported."); } @Override public Double getMinValueForProbability(double p) { throw new UnsupportedOperationException("Not supported."); } @Override public Double getValueAtCumulativeProbability(double p) throws MathException { throw new UnsupportedOperationException("Not supported."); } @Override public Double getValueAtDescendingCumulativeProbability(double p) throws MathException { throw new UnsupportedOperationException("Not supported."); } }