001    package com.softnetConsult.utils.random;
002    
003    import java.util.ArrayList;
004    import java.util.List;
005    import java.util.Random;
006    
007    import com.softnetConsult.utils.math.MathTools;
008    
009    /**
010     * Creates Poisson-distributed pseudorandom numbers.
011     * 
012     * <p style="font-size:smaller;">This product includes software developed by the
013     *    <strong>SoftNet-Consult Java Utility Library</strong> project and its contributors.<br />
014     *    (<a href="http://java-tools.sourceforge.net" target="_blank">http://java-tools.sourceforge.net</a>)<br />
015     *    Copyright (c) 2007-2008 SoftNet-Consult.<br />
016     *    Copyright (c) 2007-2008 G. Paperin.<br />
017     *    All rights reserved.
018     * </p>
019     * <p style="font-size:smaller;">File: Poisson.java<br />
020     *    Library API version: {@value com.softnetConsult.utils.APIProperties#apiVersion}<br />
021     *    Java compliance version: {@value com.softnetConsult.utils.APIProperties#javaComplianceVersion}
022     * </p>
023     * <p style="font-size:smaller;">Redistribution and use in source and binary forms, with or
024     *    without modification, are permitted provided that the following terms and conditions are met:
025     * </p>
026     * <p style="font-size:smaller;">1. Redistributions of source code must retain the above
027     *    acknowledgement of the SoftNet-Consult Java Utility Library project, the above copyright
028     *    notice, this list of conditions and the following disclaimer.<br />
029     *    2. Redistributions in binary form must reproduce the above acknowledgement of the
030     *    SoftNet-Consult Java Utility Library project, the above copyright notice, this list of
031     *    conditions and the following disclaimer in the documentation and/or other materials
032     *    provided with the distribution.<br />
033     *    3. All advertising materials mentioning features or use of this software or any derived
034     *    software must display the following acknowledgement:<br />
035     *    <em>This product includes software developed by the SoftNet-Consult Java Utility Library
036     *    project and its contributors.</em>
037     * </p>
038     * <p style="font-size:smaller;">THIS SOFTWARE IS PROVIDED &quot;AS IS&quot;, WITHOUT WARRANTY
039     *    OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
040     *    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
041     *    THE AUTHORS, CONTRIBUTORS OR COPYRIGHT  HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
042     *    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING  FROM, OUT OF OR
043     *    IN CONNECTION WITH THE SOFTWARE OR THE USE OR  OTHER DEALINGS IN THE SOFTWARE.
044     * </p> 
045     * @author Greg Paperin (<a href="http://www.paperin.org" target="_blank">http://www.paperin.org</a>)
046     * @version {@value com.softnetConsult.utils.APIProperties#apiVersion}
047     */
048    public class Poisson {
049    
050    
051    /**
052     * Default value for epsilon when no other value is specified.
053     */
054    public static final double DEFAULT_EPSILON = Math.pow(10, -10);
055    
056    
057    /**
058     * Specifies the completeness of the CDF table - the table is calculated until the
059     * approximated CDF value reaches {@code 1.0 - epsilon}.
060     */
061    private double epsilon = DEFAULT_EPSILON;
062    
063    
064    /**
065     * The lambda parameter of this Poisson distribution.
066     * Both, the mean and the variance of a Poisson-distributed random variable are equal to lambda.
067     */
068    private double lambda = 0;
069    
070    
071    /**
072     * The CDF table.
073     */
074    private double [] table = null;
075    
076    
077    /**
078     * The source of uniformly independently distributed random numbers.
079     */
080    private Random rndSource = null;
081    
082    
083    /**
084     * Creates a generator for Poisson distributed random numbers with the specified lambda-parameter.
085     * Calling this constructor is equivalent to {@code Poisson(lambda, new Random(), Poisson.DEFAULT_EPSILON)}.
086     *  
087     * @param lambda The LAMBDA-parameter to the Poisson distribution.
088     * @see #Poisson(double, Random, double)
089     */
090    public Poisson(double lambda) {
091            this(lambda, new Random(), DEFAULT_EPSILON);
092    }
093    
094    /**
095     * Creates a generator for Poisson distributed random numbers with the specified lambda-parameter
096     * and the specified source of random numbers.
097     * 
098     * Calling this constructor is equivalent to {@code Poisson(lambda, rndSource, Poisson.DEFAULT_EPSILON)}.
099     *  
100     * @param lambda The LAMBDA-parameter to the Poisson distribution.
101     * @param rndSource The source of uniformly independently distributed (pseudo-) random numbers to use for
102     * this distribution.
103     * @see #Poisson(double, Random, double)
104     */
105    public Poisson(double lambda, Random rndSource) {
106            this(lambda, rndSource, DEFAULT_EPSILON);
107    }
108    
109    
110    /**
111     * Creates a generator for Poisson distributed random numbers.
112     * 
113     * @param lambda The LAMBDA parameter for this Poisson distribution.
114     * Both, the mean and the variance of a Poisson-distributed random variable are equal to {@code lambda}.
115     * @param rndSource The source of uniformly independently distributed (pseudo-) random numbers to use for
116     * this distribution.
117     * @param epsilon Completeness threshold used during the computation of the cumulative distribution function
118     * table. The CDF table is calculated until the approximated CDF value reaches {@code 1.0 - epsilon}.
119     */
120    public Poisson(double lambda, Random rndSource, double epsilon) {
121            
122            if (1 > lambda)
123                    throw new IllegalArgumentException("Poisson parameter lambda must be >= 1.0");
124            
125            if (null == rndSource)
126                    throw new NullPointerException("Cannot use a null random source");
127            
128            if (epsilon < 0.0)
129                    throw new IllegalArgumentException("Epsilon must be a very small positive number");
130            
131            if (epsilon > 0.05)
132                    throw new IllegalArgumentException("The specified value of epsilon is too large");
133            
134            this.lambda = lambda;
135            this.rndSource = rndSource;
136            this.epsilon = epsilon;
137            
138            List<Double> tab = new ArrayList<Double>();
139            double cdf = 0, pmf = 0;
140            int k = 0;
141            do {            
142                    pmf = MathTools.poisson(lambda, k++);
143                    if (Double.isInfinite(pmf) || Double.isNaN(pmf)) {
144                            throw new IllegalArgumentException("Poisson parameter lambda (=" + lambda + ") is too large,"
145                                                                                             + " or epsilon (=" + epsilon + ") is too small:"
146                                                                                             + " the cumulative distribution table cannot be computed with"
147                                                                                             + " sufficient accuracy");
148                    }
149                    cdf += pmf;
150                    tab.add(cdf);
151            } while (cdf < 1. - epsilon);
152            
153            this.table = new double[tab.size()];
154            for (k = 0; k < this.table.length; k++)
155                    this.table[k] = tab.get(k);
156            
157            //printTable();
158    }
159    
160    /*
161    private void printTable() {
162            // For debug only:
163            for (int x = 0; x < table.length; x++) {
164                    System.out.println("PoissonCDF(lambda="+lambda+", x="+x+") = " + table[x]);
165            }
166    }
167    */
168    
169    /**
170     * Returns expected mean and variance of this Poisson distribution.
171     * 
172     * @return The LAMBDA parameter of this Poisson distribution.
173     */
174    public double getLambda() {
175            return lambda;
176    }
177    
178    /**
179     * The epsilon value used during the calculation of the CDF table.
180     *  
181     * @return Completeness threshold used during the computation of the cumulative distribution function
182     * table. The CDF table is calculated until the approximated CDF value reaches {@code 1.0 - epsilon}.
183     */
184    public double getEpsilon() {
185            return epsilon;
186    }
187    
188    /**
189     * Generates a new (pseudo-) random integer number from this Poisson-distribution.
190     *   
191     * @return A new Poisson-distributed integer.
192     */
193    public int nextRandom() {
194            double dice = rndSource.nextDouble();
195            return tableSearch(dice);
196    }
197    
198    
199    /**
200     * Used internally to look up the value of the inverse of the cumulative distribution function
201     * of the Poisson distribution.
202     *  
203     * @param dice A number in interval [0, 1]
204     * @return The smallest integer <em>k</em> such that <em>{@code dice} <= CDF(k)</em>,
205     * where <em>CDF</em> is the cumulative distribution function of this Poisson distribution. 
206     */
207    private int tableSearch(double dice) {
208            int left = 0;
209            int right = table.length - 1;
210            
211            while (left <= right) {
212                    int pivot = (left + right) >>> 1;
213                    double pivotVal = table[pivot];
214    
215                    if (pivotVal < dice)
216                            left = pivot + 1;
217                    else if (pivotVal > dice)
218                            right = pivot - 1;
219                    else
220                            return pivot;
221            }
222            return left;
223    }
224    
225    } // public class Poisson