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