/** * \file ExponentialProb.hpp * \brief Header for ExponentialProb * * Return true with probabililty exp(-\e p). * * Copyright (c) Charles Karney (2006-2012) and licensed * under the MIT/X11 License. For more information, see * http://randomlib.sourceforge.net/ **********************************************************************/ #if !defined(RANDOMLIB_EXPONENTIALPROB_HPP) #define RANDOMLIB_EXPONENTIALPROB_HPP 1 #include #include #if defined(_MSC_VER) // Squelch warnings about constant conditional expressions # pragma warning (push) # pragma warning (disable: 4127) #endif namespace RandomLib { /** * \brief The exponential probability. * * Return true with probability exp(−\e p). Basic method taken from:\n * J. von Neumann,\n Various Techniques used in Connection with Random * Digits,\n J. Res. Nat. Bur. Stand., Appl. Math. Ser. 12, 36--38 * (1951),\n reprinted in Collected Works, Vol. 5, 768--770 (Pergammon, * 1963).\n See also the references given for the ExactExponential class. * * Here the method is extended to be exact by generating sufficient bits in * the random numbers in the algorithm to allow the unambiguous comparisons * to be made. * * Here's one way of sampling from a normal distribution with zero mean and * unit variance in the interval [−1,1] with reasonable accuracy: * \code #include #include double Normal(RandomLib::Random& r) { double x; RandomLib::ExponentialProb e; do x = r.FloatW(); while ( !e(r, - 0.5 * x * x) ); return x; } \endcode * (Note that the ExactNormal class samples from the normal distribution * exactly.) * * This class uses a mutable private vector. So a single ExponentialProb * object cannot safely be used by multiple threads. In a multi-processing * environment, each thread should use a thread-specific ExponentialProb * object. **********************************************************************/ class ExponentialProb { private: typedef unsigned word; public: ExponentialProb() : _v(std::vector(alloc_incr)) {} /** * Return true with probability exp(−\e p). Returns false if \e p * ≤ 0. For in \e p (0,1], it requires about exp(\e p) random deviates. * For \e p large, it requires about exp(1)/(1 − exp(−1)) * random deviates. * * @tparam RealType the real type of the argument. * @tparam Random the type of the random generator. * @param[in,out] r a random generator. * @param[in] p the probability. * @return true with probability \e p. **********************************************************************/ template bool operator()(Random& r, RealType p) const; private: /** * Return true with probability exp(−\e p) for \e p in [0,1]. **********************************************************************/ template bool ExpFraction(Random& r, RealType p) const; /** * Holds as much of intermediate uniform deviates as needed. **********************************************************************/ mutable std::vector _v; /** * Increment on size of _v. **********************************************************************/ static const unsigned alloc_incr = 16; }; template bool ExponentialProb::operator()(Random& r, RealType p) const { STATIC_ASSERT(!std::numeric_limits::is_integer, "ExponentialProb(): invalid real type RealType"); return p <= 0 || // True if p <=0 // Ensure p - 1 < p. Also deal with IsNaN(p) ( p < RealType(1)/std::numeric_limits::epsilon() && // exp(a+b) = exp(a) * exp(b) ExpFraction(r, p < RealType(1) ? p : RealType(1)) && ( p <= RealType(1) || operator()(r, p - RealType(1)) ) ); } template bool ExponentialProb::ExpFraction(Random& r, RealType p) const { // Base of _v is 2^c. Adjust so that word(p) doesn't lose precision. static const int c = // The Intel compiler needs this to be static?? std::numeric_limits::digits < std::numeric_limits::digits ? std::numeric_limits::digits : std::numeric_limits::digits; // m gives number of valid words in _v unsigned m = 0, l = unsigned(_v.size()); if (p < RealType(1)) while (true) { if (p <= RealType(0)) return true; // p in (0, 1) if (l == m) _v.resize(l += alloc_incr); _v[m++] = r.template Integer(); p *= std::pow(RealType(2), c); // p in (0, 2^c) word w = word(p); // w in [0, 2^c) if (_v[m - 1] > w) return true; else if (_v[m - 1] < w) break; else // _v[m - 1] == w p -= RealType(w); // p in [0, 1) } // Here _v < p. Now loop finding decreasing V. Exit when first increasing // one is found. for (unsigned s = 0; ; s ^= 1) { // Parity of loop count for (unsigned j = 0; ; ++j) { if (j == m) { // Need more bits in the old V if (l == m) _v.resize(l += alloc_incr); _v[m++] = r.template Integer(); } word w = r.template Integer(); if (w > _v[j]) return s != 0u; // New V is bigger, so exit else if (w < _v[j]) { _v[j] = w; // New V is smaller, update _v m = j + 1; // adjusting its size break; // and generate the next V } // Else w == _v[j] and we need to check the next c bits } } } } // namespace RandomLib #if defined(_MSC_VER) # pragma warning (pop) #endif #endif // RANDOMLIB_EXPONENTIALPROB_HPP