ExactExponential.hpp
Go to the documentation of this file.
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