00001 /** 00002 * \file ExactExponential.hpp 00003 * \brief Header for ExactExponential 00004 * 00005 * Sample exactly from an exponential distribution. 00006 * 00007 * Written by <a href="http://charles.karney.info/">Charles Karney</a> 00008 * <charles@karney.com> and licensed under the LGPL. For more 00009 * information, see http://charles.karney.info/random/ 00010 **********************************************************************/ 00011 00012 #if !defined(EXACTEXPONENTIAL_HPP) 00013 #define EXACTEXPONENTIAL_HPP "$Id: ExactExponential.hpp 6505 2009-01-12 03:34:23Z ckarney $" 00014 00015 #include "RandomLib/RandomNumber.hpp" 00016 00017 namespace RandomLib { 00018 /** 00019 * \brief Sample exactly from an exponential distribution. 00020 * 00021 * Sample \e x >= 0 from exp(-\e x). Basic method taken from:\n J. von 00022 * Neumann,\n Various Techniques used in Connection with Random Digits,\n 00023 * J. Res. Nat. Bur. Stand., Appl. Math. Ser. 12, 36-38 (1951),\n reprinted 00024 * in Collected Works, Vol. 5, 768-770 (Pergammon, 1963).\n See also:\n 00025 * M. Abramowitz and I. A. Stegun,\n Handbook of Mathematical Functions\n 00026 * (National Bureau of Standards, 1964), Sec. 26.8.6.c.2.\n R. E. Forsythe,\n 00027 * Von Neumann's Comparison Method for Random Sampling from Normal and Other 00028 * Distributions,\n Math. Comp. 26, 817-826 (1972).\n Knuth, TAOCP, Vol 2, 00029 * Sec 3.4.1.C.3. 00030 * 00031 * The following code illustrates the basic method given by von Neumann: 00032 * \code 00033 * // Return a random number x >= 0 distributed with probability exp(-x). 00034 * double ExpDist(RandomLib::Random& r) { 00035 * for (unsigned k = 0; ; ++k) { 00036 * double x = r.Fixed(), p = x; // executed 1/(1-exp(-1)) times on avg. 00037 * for (unsigned s = 1; ; s ^= 1) { // Parity of loop count 00038 * double q = r.Fixed(); // executed exp(p) times on average 00039 * if (q >= p) 00040 * if (s) 00041 * return double(k) + x; 00042 * else 00043 * break; 00044 * p = q; 00045 * } 00046 * } 00047 * } 00048 * \endcode 00049 * This returns a result consuming exp(1)/(1 - exp(-1)) = 4.30 random 00050 * numbers on average. (Von Neumann incorrectly states that the method takes 00051 * (1 + exp(1))/(1 - exp(-1)) = 5.88 random numbers on average.) Because of 00052 * the finite precision of Random::Fixed(), the code snippet above only 00053 * approximates exp(-\e x). Instead, it returns \e x with probability \e 00054 * h(1 - \e h)<sup><i>x</i>/<i>h</i></sup> for \e x = \e ih, \e h = 00055 * 2<sup>-53</sup>, and integer \e i >= 0. 00056 * 00057 * The above is precisely von Neumann's method. Abramowitz and Stegun 00058 * consider a variant: sample uniform variants until the first is less 00059 * than the sum of the rest. Forsythe converts the > ranking for the runs to 00060 * >= which makes the analysis of the discrete case more difficult. He also 00061 * drops the "trick" by which the integer part of the deviate is given by the 00062 * number of rejections when finding the fractional part. 00063 * 00064 * Von Neumann says of this method: "The machine has in effect computed a 00065 * logarithm by performing only discriminations on the relative magnitude of 00066 * numbers in (0,1). It is a sad fact of life, however, that under the 00067 * particular conditions of the Eniac it was slightly quicker to use a 00068 * truncated power series for log(1-\e T) than to carry out all the 00069 * discriminations. In conclusion, I should like to mention that the above 00070 * method can be modified to yield a distribution satisfying any first-order 00071 * differential equation." Forsythe attempts to extend the method along the 00072 * lines of the previous sentence. However, he merely ends up with a 00073 * rejection method where the acceptance probability is exp(\e f(\e x)) and 00074 * one is left wondering whether von Neumann had a less trivial extension in 00075 * mind. 00076 * 00077 * Here the method is extended to use infinite precision uniform deviates 00078 * implemented by RandomNumber and returning \e exact results for the 00079 * exponential distribution. This is possible because only comparisions are 00080 * done in the method. The template parameter \a bits specifies the number 00081 * of bits in the base used for RandomNumber (i.e., base = 00082 * 2<sup><i>bits</i></sup>). 00083 * 00084 * For example the following samples from an exponential distribution and 00085 * prints various representations of the result. 00086 * \code 00087 * #include "RandomLib/RandomNumber.hpp" 00088 * #include "RandomLib/ExactExponential.hpp" 00089 * 00090 * RandomLib::Random r; 00091 * const int bits = 1; 00092 * RandomLib::ExactExponential<bits> edist; 00093 * for (size_t i = 0; i < 10; ++i) { 00094 * RandomLib::RandomNumber<bits> x = edist(r); // Sample 00095 * std::pair<double, double> z = x.Range(); 00096 * std::cout << x << " = " // Print in binary with ellipsis 00097 * << "(" << z.first << "," << z.second << ")"; // Print range 00098 * double v = x.Value<double>(r); // Round exactly to nearest double 00099 * std::cout << " = " << v << std::endl; 00100 * } 00101 * \endcode 00102 * Here's a possible result: 00103 \verbatim 00104 0.0111... = (0.4375,0.5) = 0.474126 00105 10.000... = (2,2.125) = 2.05196 00106 1.00... = (1,1.25) = 1.05766 00107 0.010... = (0.25,0.375) = 0.318289 00108 10.1... = (2.5,3) = 2.8732 00109 0.0... = (0,0.5) = 0.30753 00110 0.101... = (0.625,0.75) = 0.697654 00111 0.00... = (0,0.25) = 0.0969214 00112 0.0... = (0,0.5) = 0.194053 00113 0.11... = (0.75,1) = 0.867946 00114 \endverbatim 00115 **********************************************************************/ 00116 template<int bits = 1> class ExactExponential { 00117 public: 00118 /** 00119 * Return a random deviate with an exponential distribution, exp(-\e x) for 00120 * \e x >= 0. Requires 9.316 bits per invocation for \a bits = 1, 6.03 00121 * digits for \a bits = 2, 5.06 digits for \a bits = 3, and 4.30 = 00122 * exp(1)/(1 - exp(-1)) digits for large \a bits. The frequency of bits in 00123 * the fractional part of the returned result with \a bits = 1:\n 00124 \verbatim 00125 bits freq(%) 00126 1 47.98 00127 2 25.50 00128 3 13.13 00129 4 6.66 00130 5 3.36 00131 6 1.68 00132 7 0.84 00133 8 0.42 00134 9 0.21 00135 10 0.11 00136 11 0.05 00137 12 0.03 00138 13+ 0.03 00139 \endverbatim 00140 * The average number of bits in fraction = 2.054. The frequency table of 00141 * \a bits > 1 can be generated from the table above. E.g., with \a bits = 00142 * 2, the result consists of three digits with frequency 3.36% + 1.68% = 00143 * 5.04%. The relative frequency of the results for the fractional part 00144 * with \a bits = 1 can be shown via a histogram\n <img src="exphist.png" 00145 * width=580 height=750 alt="exact binary sampling of exponential 00146 * distribution">\n The base of each rectangle gives the range represented 00147 * by the corresponding binary number and the area is proportional to its 00148 * frequency. A PDF version of this figure is given 00149 * <a href="exphist.pdf">here</a>. This allows the figure to be magnified 00150 * to show the rectangles for all binary numbers up to 9 bits. 00151 **********************************************************************/ 00152 template<class Random> RandomNumber<bits> operator()(Random& r) const; 00153 private: 00154 /** 00155 * Return true with probability exp(-\a p) for \a p in (0,1). For p = 00156 * (0,1), uses 5.89 random bits per invocation for \a bits = 1, 3.81 digits 00157 * for \a bits = 2, 3.20 digits for \a bits = 3, and 2.72 = exp(1) digits 00158 * for \a bits large. (5.89 is in [5.888, 5.889]. This is close to but 00159 * not equal to (1 + exp(1))/(1 - exp(-1)) = 5.882.) 00160 **********************************************************************/ 00161 template<class Random> bool 00162 ExpFraction(Random& r, RandomNumber<bits>& p) const; 00163 }; 00164 00165 template<int bits> template<class Random> inline RandomNumber<bits> 00166 ExactExponential<bits>::operator()(Random& r) const { 00167 // A simple rejection method gives the fraction part. The number of 00168 // rejections gives the integer part. 00169 RandomNumber<bits> x; 00170 int k = 0; 00171 while (!ExpFraction(r, x)) { // Executed 1/(1 - exp(-1)) on average 00172 ++k; 00173 x.Init(); 00174 } 00175 x.SetInteger(k); 00176 return x; 00177 } 00178 00179 template<int bits> template<class Random> inline bool 00180 ExactExponential<bits>::ExpFraction(Random& r, RandomNumber<bits>& p) 00181 const { 00182 // Implement the von Neumann algorithm. 00183 RandomNumber<bits> w; // w = r.Fixed(); 00184 if (!w.LessThan(r, p)) // if (w < p) 00185 return true; 00186 RandomNumber<bits> v; 00187 while (true) { // Unroll loop to avoid copying RandomNumber 00188 v.Init(); // v = r.Fixed(); 00189 if (!v.LessThan(r, w)) // if (v < w) 00190 return false; 00191 w.Init(); // w = r.Fixed(); 00192 if (!w.LessThan(r, v)) // if (w < v) 00193 return true; 00194 } 00195 } 00196 } // namespace RandomLib 00197 #endif // EXACTEXPONENTIAL_HPP