00001 /*
00002 * This program is free software; you can redistribute it and/or modify
00003 * it under the terms of the GNU General Public License as published by
00004 * the Free Software Foundation; either version 2 of the License, or
00005 * (at your option) any later version.
00006 *
00007 * This program is distributed in the hope that it will be useful,
00008 * but WITHOUT ANY WARRANTY; without even the implied warranty of
00009 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00010 * GNU General Public License for more details.
00011 *
00012 * You should have received a copy of the GNU General Public License
00013 * along with this program; if not, write to the Free Software
00014 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00015 *
00016 * This file is extracted from the random generator library provided by Agner Fog
00017 * (http://www.agner.org/random/) and has been modified for Seagull.
00018 * (c) 2002 Agner Fog. GNU General Public License www.gnu.org/copyleft/gpl.html
00019 *
00020 * (c)Copyright 2006 Hewlett-Packard Development Company, LP.
00021 *
00022 */
00023
00024 #include <math.h> // math functions
00025 #include "stocc.h" // class definition
00026
00027 /***********************************************************************
00028 Log factorial function
00029 ***********************************************************************/
00030 double LnFac(int32 n) {
00031 // log factorial function. gives natural logarithm of n!
00032
00033 // define constants
00034 static const double // coefficients in Stirling approximation
00035 C0 = 0.918938533204672722, // ln(sqrt(2*pi))
00036 C1 = 1./12.,
00037 C3 = -1./360.;
00038 // C5 = 1./1260., // use r^5 term if FAK_LEN < 50
00039 // C7 = -1./1680.; // use r^7 term if FAK_LEN < 20
00040 // static variables
00041 static double fac_table[FAK_LEN]; // table of ln(n!):
00042 static int initialized = 0; // remember if fac_table has been initialized
00043
00044 if (n < FAK_LEN) {
00045 if (n <= 1) {
00046 if (n < 0) FatalError("Parameter negative in LnFac function");
00047 return 0;}
00048 if (!initialized) { // first time. Must initialize table
00049 // make table of ln(n!)
00050 double sum = fac_table[0] = 0.;
00051 for (int i=1; i<FAK_LEN; i++) {
00052 sum += log(float(i));
00053 fac_table[i] = sum;}
00054 initialized = 1;}
00055 return fac_table[n];}
00056
00057 // not found in table. use Stirling approximation
00058 double n1, r;
00059 n1 = n; r = 1. / n1;
00060 return (n1 + 0.5)*log(n1) - n1 + C0 + r*(C1 + r*r*C3);}
00061
00062
00063 /***********************************************************************
00064 constants
00065 ***********************************************************************/
00066 const double SHAT1 = 2.943035529371538573; // 8/e
00067 const double SHAT2 = 0.8989161620588987408; // 3-sqrt(12/e)
00068
00069 /***********************************************************************
00070 Poisson distribution
00071 ***********************************************************************/
00072 int32 StochasticLib1::Poisson (double L) {
00073 /*
00074 This function generates a random variate with the poisson distribution.
00075
00076 Uses inversion by chop-down method for L < 17, and ratio-of-uniforms
00077 method for L >= 17.
00078
00079 For L < 1.E-6 numerical inaccuracy is avoided by direct calculation.
00080 */
00081
00082 //------------------------------------------------------------------
00083 // choose method
00084 //------------------------------------------------------------------
00085 if (L < 17) {
00086 if (L < 1.E-6) {
00087 if (L == 0) return 0;
00088 if (L < 0) FatalError("Parameter negative in poisson function");
00089
00090 //--------------------------------------------------------------
00091 // calculate probabilities
00092 //--------------------------------------------------------------
00093 // For extremely small L we calculate the probabilities of x = 1
00094 // and x = 2 (ignoring higher x). The reason for using this
00095 // method is to prevent numerical inaccuracies in other methods.
00096 //--------------------------------------------------------------
00097 return PoissonLow(L);}
00098
00099 else {
00100
00101 //--------------------------------------------------------------
00102 // inversion method
00103 //--------------------------------------------------------------
00104 // The computation time for this method grows with L.
00105 // Gives overflow for L > 80
00106 //--------------------------------------------------------------
00107 return PoissonInver(L);}}
00108
00109 else {
00110 if (L > 2.E9) FatalError("Parameter too big in poisson function");
00111
00112 //----------------------------------------------------------------
00113 // ratio-of-uniforms method
00114 //----------------------------------------------------------------
00115 // The computation time for this method does not depend on L.
00116 // Use where other methods would be slower.
00117 //----------------------------------------------------------------
00118 return PoissonRatioUniforms(L);}}
00119
00120
00121 /***********************************************************************
00122 Subfunctions used by poisson
00123 ***********************************************************************/
00124 int32 StochasticLib1::PoissonLow(double L) {
00125 /*
00126 This subfunction generates a random variate with the poisson
00127 distribution for extremely low values of L.
00128
00129 The method is a simple calculation of the probabilities of x = 1
00130 and x = 2. Higher values are ignored.
00131
00132 The reason for using this method is to avoid the numerical inaccuracies
00133 in other methods.
00134 */
00135 double d, r;
00136 d = sqrt(L);
00137 if (Random() >= d) return 0;
00138 r = Random() * d;
00139 if (r > L * (1.-L)) return 0;
00140 if (r > 0.5 * L*L * (1.-L)) return 1;
00141 return 2;}
00142
00143 int32 StochasticLib1::PoissonInver(double L) {
00144 /*
00145 This subfunction generates a random variate with the poisson
00146 distribution using inversion by the chop down method (PIN).
00147
00148 Execution time grows with L. Gives overflow for L > 80.
00149
00150 The value of bound must be adjusted to the maximal value of L.
00151 */
00152 const int bound = 130; // safety bound. Must be > L + 8*sqrt(L).
00153 static double p_L_last = -1.; // previous value of L
00154 static double p_f0; // value at x=0
00155 double r; // uniform random number
00156 double f; // function value
00157 int32 x; // return value
00158
00159 if (L != p_L_last) { // set up
00160 p_L_last = L;
00161 p_f0 = exp(-L);} // f(0) = probability of x=0
00162
00163 while (1) {
00164 r = Random(); x = 0; f = p_f0;
00165 do { // recursive calculation: f(x) = f(x-1) * L / x
00166 r -= f;
00167 if (r <= 0) return x;
00168 x++;
00169 f *= L;
00170 r *= x;} // instead of f /= x
00171 while (x <= bound);}}
00172
00173 int32 StochasticLib1::PoissonRatioUniforms(double L) {
00174 /*
00175 This subfunction generates a random variate with the poisson
00176 distribution using the ratio-of-uniforms rejection method (PRUAt).
00177
00178 Execution time does not depend on L, except that it matters whether L
00179 is within the range where ln(n!) is tabulated.
00180
00181 Reference: E. Stadlober: "The ratio of uniforms approach for generating
00182 discrete random variates". Journal of Computational and Applied Mathematics,
00183 vol. 31, no. 1, 1990, pp. 181-189.
00184 */
00185 static double p_L_last = -1.0; // previous L
00186 static double p_a; // hat center
00187 static double p_h; // hat width
00188 static double p_g; // ln(L)
00189 static double p_q; // value at mode
00190 static int32 p_bound; // upper bound
00191 int32 mode; // mode
00192 double u; // uniform random
00193 double lf; // ln(f(x))
00194 double x; // real sample
00195 int32 k; // integer sample
00196
00197 if (p_L_last != L) {
00198 p_L_last = L; // Set-up
00199 p_a = L + 0.5; // hat center
00200 mode = (int32)L; // mode
00201 p_g = log(L);
00202 p_q = mode * p_g - LnFac(mode); // value at mode
00203 p_h = sqrt(SHAT1 * (L+0.5)) + SHAT2; // hat width
00204 p_bound = (int32)(p_a + 6.0 * p_h);} // safety-bound
00205
00206 while(1) {
00207 u = Random();
00208 if (u == 0) continue; // avoid division by 0
00209 x = p_a + p_h * (Random() - 0.5) / u;
00210 if (x < 0 || x >= p_bound) continue; // reject if outside valid range
00211 k = (int32)(x);
00212 lf = k * p_g - LnFac(k) - p_q;
00213 if (lf >= u * (4.0 - u) - 3.0) break; // quick acceptance
00214 if (u * (u - lf) > 1.0) continue; // quick rejection
00215 if (2.0 * log(u) <= lf) break;} // final acceptance
00216 return(k);}
00217
00218
00219 /***********************************************************************
00220 Constructor
00221 ***********************************************************************/
00222 StochasticLib1::StochasticLib1 (int seed)
00223 : RANDOM_GENERATOR(seed) {
00224 normal_x2_valid = 0;}
00225
00226
00227
1.2.14 written by Dimitri van Heesch,
© 1997-2002