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