ExponentialProb.hpp
Go to the documentation of this file.
00001 /**
00002  * \file ExponentialProb.hpp
00003  * \brief Header for ExponentialProb
00004  *
00005  * Return true with probabililty exp(-\e p).
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(EXPONENTIALPROB_HPP)
00012 #define EXPONENTIALPROB_HPP "$Id: ExponentialProb.hpp 6723 2010-01-11 14:20:10Z ckarney $"
00013 
00014 #include <vector>
00015 #include <limits>
00016 
00017 namespace RandomLib {
00018   /**
00019    * \brief The exponential probability.
00020    *
00021    * Return true with probability exp(-\e p).  Basic method taken from:\n
00022    * J. von Neumann,\n Various Techniques used in Connection with Random
00023    * Digits,\n J. Res. Nat. Bur. Stand., Appl. Math. Ser. 12, 36-38 (1951),\n
00024    * reprinted in Collected Works, Vol. 5, 768-770 (Pergammon, 1963).\n See
00025    * also the references given for the ExactExponential class.
00026    *
00027    * Here the method is extended to be exact by generating sufficient bits in
00028    * the random numbers in the algorithm to allow the unambiguous comparisons
00029    * to be made.
00030    *
00031    * Here's one way of sampling from a normal distribution with zero mean and
00032    * unit variance in the interval [-1,1] with reasonable accuracy:
00033    * \code
00034    * #include "RandomLib/Random.hpp"
00035    * #include "RandomLib/ExponentialProb.hpp"
00036    *
00037    * double Normal(RandomLib::Random& r) {
00038    *   double x;
00039    *   RandomLib::ExponentialProb e;
00040    *   do
00041    *      x = r.FloatW();
00042    *   while ( !e(r, - 0.5 * x * x) );
00043    *   return x;
00044    * }
00045    * \endcode
00046    **********************************************************************/
00047   class ExponentialProb {
00048   private:
00049     typedef unsigned word;
00050   public:
00051 
00052     ExponentialProb() : _v(std::vector<word>(alloc_incr)) {}
00053     /**
00054      * Return true with probability exp(-\e p).  Returns false if \e p <= 0.
00055      * For in \e p (0,1], it requires about exp(\e p) random deviates.  For \e
00056      * p large, it requires about exp(1)/(1 - exp(-1)) random deviates.
00057      **********************************************************************/
00058     template<typename RealType, class Random>
00059     bool operator()(Random& r, RealType p) const throw(std::bad_alloc);
00060 
00061   private:
00062     /**
00063      * Return true with probability exp(-\e p) for \e p in [0,1].
00064      **********************************************************************/
00065     template<typename RealType, class Random>
00066     bool ExpFraction(Random& r, RealType p) const throw(std::bad_alloc);
00067     /**
00068      * Holds as much of intermediate uniform deviates as needed.
00069      **********************************************************************/
00070     mutable std::vector<word> _v;
00071     /**
00072      * Increment on size of _v.
00073      **********************************************************************/
00074     static const unsigned alloc_incr = 16;
00075   };
00076 
00077   template<typename RealType, class Random>
00078   inline bool ExponentialProb::operator()(Random& r, RealType p) const
00079     throw(std::bad_alloc) {
00080     return p <= 0 ||            // True if p <=0
00081       // Ensure p - 1 < p.  Also deal with IsNaN(p)
00082       p < RealType(1)/std::numeric_limits<RealType>::epsilon() &&
00083       // exp(a+b) = exp(a) * exp(b)
00084       ExpFraction(r, p < RealType(1) ? p : RealType(1)) &&
00085       ( p <= RealType(1) || operator()(r, p - RealType(1)) );
00086   }
00087 
00088   template<typename RealType, class Random>
00089   inline bool ExponentialProb::ExpFraction(Random& r, RealType p) const
00090     throw(std::bad_alloc) {
00091     // Base of _v is 2^c.  Adjust so that word(p) doesn't lose precision.
00092     const int c =
00093       std::numeric_limits<word>::digits <
00094       std::numeric_limits<RealType>::digits ?
00095       std::numeric_limits<word>::digits :
00096       std::numeric_limits<RealType>::digits;
00097     // m gives number of valid words in _v
00098     unsigned m = 0, l = _v.size();
00099     if (p < RealType(1))
00100       while (true) {
00101         if (p <= RealType(0))
00102           return true;
00103         // p in (0, 1)
00104         if (l == m)
00105           _v.resize(l += alloc_incr);
00106         _v[m++] = r.template Integer<word, c>();
00107         p *= pow(RealType(2), c); // p in (0, 2^c)
00108         word w = word(p);       // w in [0, 2^c)
00109         if (_v[m - 1] > w)
00110           return true;
00111         else if (_v[m - 1] < w)
00112           break;
00113         else                    // _v[m - 1] == w
00114           p -= RealType(w);     // p in [0, 1)
00115       }
00116     // Here _v < p.  Now loop finding decreasing V.  Exit when first increasing
00117     // one is found.
00118     for (unsigned s = 0; ; s ^= 1) { // Parity of loop count
00119       for (size_t j = 0; ; ++j) {
00120         if (j == m) {
00121           // Need more bits in the old V
00122           if (l == m)
00123             _v.resize(l += alloc_incr);
00124           _v[m++] = r.template Integer<word, c>();
00125         }
00126         word w = r.template Integer<word, c>();
00127         if (w > _v[j])
00128           return s;             // New V is bigger, so exit
00129         else if (w < _v[j]) {
00130           _v[j] = w;            // New V is smaller, update _v
00131           m = j + 1;            // adjusting its size
00132           break;                // and generate the next V
00133         }
00134         // Else w == _v[j] and we need to check the next c bits
00135       }
00136     }
00137   }
00138 } // namespace RandomLib
00139 #endif  // EXPONENTIALPROB_HPP