X-Git-Url: https://git.donarmstrong.com/?p=rsem.git;a=blobdiff_plain;f=boost%2Frandom%2Fbinomial_distribution.hpp;fp=boost%2Frandom%2Fbinomial_distribution.hpp;h=dbe2784d2a65e938a9c5e316a86afdb68cbfc62d;hp=f0e46a50eaa3f01e59565efa506b2cd8dffaead8;hb=2d71eb92104693ca9baa5a2e1c23eeca776d8fd3;hpb=da57529b92adbb7ae74a89861cb39fb35ac7c62d diff --git a/boost/random/binomial_distribution.hpp b/boost/random/binomial_distribution.hpp index f0e46a5..dbe2784 100644 --- a/boost/random/binomial_distribution.hpp +++ b/boost/random/binomial_distribution.hpp @@ -1,111 +1,422 @@ /* boost random/binomial_distribution.hpp header file * - * Copyright Jens Maurer 2002 + * Copyright Steven Watanabe 2010 * Distributed under the Boost Software License, Version 1.0. (See * accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * See http://www.boost.org for most recent version including documentation. * - * $Id: binomial_distribution.hpp 60755 2010-03-22 00:45:06Z steven_watanabe $ - * + * $Id: binomial_distribution.hpp 71018 2011-04-05 21:27:52Z steven_watanabe $ */ -#ifndef BOOST_RANDOM_BINOMIAL_DISTRIBUTION_HPP -#define BOOST_RANDOM_BINOMIAL_DISTRIBUTION_HPP +#ifndef BOOST_RANDOM_BINOMIAL_DISTRIBUTION_HPP_INCLUDED +#define BOOST_RANDOM_BINOMIAL_DISTRIBUTION_HPP_INCLUDED #include -#include +#include +#include + #include -#include +#include + +#include namespace boost { +namespace random { + +namespace detail { + +template +struct binomial_table { + static const RealType table[10]; +}; + +template +const RealType binomial_table::table[10] = { + 0.08106146679532726, + 0.04134069595540929, + 0.02767792568499834, + 0.02079067210376509, + 0.01664469118982119, + 0.01387612882307075, + 0.01189670994589177, + 0.01041126526197209, + 0.009255462182712733, + 0.008330563433362871 +}; + +} /** * The binomial distribution is an integer valued distribution with * two parameters, @c t and @c p. The values of the distribution * are within the range [0,t]. * - * The probability that the distribution produces a value k is - * \f${t \choose k}p^k(1-p)^{t-k}\f$. + * The distribution function is + * \f$\displaystyle P(k) = {t \choose k}p^k(1-p)^{t-k}\f$. + * + * The algorithm used is the BTRD algorithm described in + * + * @blockquote + * "The generation of binomial random variates", Wolfgang Hormann, + * Journal of Statistical Computation and Simulation, Volume 46, + * Issue 1 & 2 April 1993 , pages 101 - 110 + * @endblockquote */ template -class binomial_distribution -{ +class binomial_distribution { public: - typedef typename bernoulli_distribution::input_type input_type; - typedef IntType result_type; - - /** - * Construct an @c binomial_distribution object. @c t and @c p - * are the parameters of the distribution. - * - * Requires: t >=0 && 0 <= p <= 1 - */ - explicit binomial_distribution(IntType t = 1, - const RealType& p = RealType(0.5)) - : _bernoulli(p), _t(t) - { - assert(_t >= 0); - assert(RealType(0) <= p && p <= RealType(1)); - } - - // compiler-generated copy ctor and assignment operator are fine - - /** Returns: the @c t parameter of the distribution */ - IntType t() const { return _t; } - /** Returns: the @c p parameter of the distribution */ - RealType p() const { return _bernoulli.p(); } - /** - * Effects: Subsequent uses of the distribution do not depend - * on values produced by any engine prior to invoking reset. - */ - void reset() { } - - /** - * Returns: a random variate distributed according to the - * binomial distribution. - */ - template - result_type operator()(Engine& eng) - { - // TODO: This is O(_t), but it should be O(log(_t)) for large _t - result_type n = 0; - for(IntType i = 0; i < _t; ++i) - if(_bernoulli(eng)) - ++n; - return n; - } + typedef IntType result_type; + typedef RealType input_type; + class param_type { + public: + typedef binomial_distribution distribution_type; + /** + * Construct a param_type object. @c t and @c p + * are the parameters of the distribution. + * + * Requires: t >=0 && 0 <= p <= 1 + */ + explicit param_type(IntType t_arg = 1, RealType p_arg = RealType (0.5)) + : _t(t_arg), _p(p_arg) + {} + /** Returns the @c t parameter of the distribution. */ + IntType t() const { return _t; } + /** Returns the @c p parameter of the distribution. */ + RealType p() const { return _p; } #ifndef BOOST_RANDOM_NO_STREAM_OPERATORS - /** - * Writes the parameters of the distribution to a @c std::ostream. - */ - template - friend std::basic_ostream& - operator<<(std::basic_ostream& os, const binomial_distribution& bd) - { - os << bd._bernoulli << " " << bd._t; - return os; - } - - /** - * Reads the parameters of the distribution from a @c std::istream. - */ - template - friend std::basic_istream& - operator>>(std::basic_istream& is, binomial_distribution& bd) - { - is >> std::ws >> bd._bernoulli >> std::ws >> bd._t; - return is; - } + /** Writes the parameters of the distribution to a @c std::ostream. */ + template + friend std::basic_ostream& + operator<<(std::basic_ostream& os, + const param_type& parm) + { + os << parm._p << " " << parm._t; + return os; + } + + /** Reads the parameters of the distribution from a @c std::istream. */ + template + friend std::basic_istream& + operator>>(std::basic_istream& is, param_type& parm) + { + is >> parm._p >> std::ws >> parm._t; + return is; + } #endif + /** Returns true if the parameters have the same values. */ + friend bool operator==(const param_type& lhs, const param_type& rhs) + { + return lhs._t == rhs._t && lhs._p == rhs._p; + } + /** Returns true if the parameters have different values. */ + friend bool operator!=(const param_type& lhs, const param_type& rhs) + { + return !(lhs == rhs); + } + private: + IntType _t; + RealType _p; + }; + + /** + * Construct a @c binomial_distribution object. @c t and @c p + * are the parameters of the distribution. + * + * Requires: t >=0 && 0 <= p <= 1 + */ + explicit binomial_distribution(IntType t_arg = 1, + RealType p_arg = RealType(0.5)) + : _t(t_arg), _p(p_arg) + { + init(); + } + + /** + * Construct an @c binomial_distribution object from the + * parameters. + */ + explicit binomial_distribution(const param_type& parm) + : _t(parm.t()), _p(parm.p()) + { + init(); + } + + /** + * Returns a random variate distributed according to the + * binomial distribution. + */ + template + IntType operator()(URNG& urng) const + { + if(use_inversion()) { + if(0.5 < _p) { + return _t - invert(_t, 1-_p, urng); + } else { + return invert(_t, _p, urng); + } + } else if(0.5 < _p) { + return _t - generate(urng); + } else { + return generate(urng); + } + } + + /** + * Returns a random variate distributed according to the + * binomial distribution with parameters specified by @c param. + */ + template + IntType operator()(URNG& urng, const param_type& parm) const + { + return binomial_distribution(parm)(urng); + } + + /** Returns the @c t parameter of the distribution. */ + IntType t() const { return _t; } + /** Returns the @c p parameter of the distribution. */ + RealType p() const { return _p; } + + /** Returns the smallest value that the distribution can produce. */ + IntType min BOOST_PREVENT_MACRO_SUBSTITUTION() const { return 0; } + /** Returns the largest value that the distribution can produce. */ + IntType max BOOST_PREVENT_MACRO_SUBSTITUTION() const { return _t; } + + /** Returns the parameters of the distribution. */ + param_type param() const { return param_type(_t, _p); } + /** Sets parameters of the distribution. */ + void param(const param_type& parm) + { + _t = parm.t(); + _p = parm.p(); + init(); + } + + /** + * Effects: Subsequent uses of the distribution do not depend + * on values produced by any engine prior to invoking reset. + */ + void reset() { } + +#ifndef BOOST_RANDOM_NO_STREAM_OPERATORS + /** Writes the parameters of the distribution to a @c std::ostream. */ + template + friend std::basic_ostream& + operator<<(std::basic_ostream& os, + const binomial_distribution& bd) + { + os << bd.param(); + return os; + } + + /** Reads the parameters of the distribution from a @c std::istream. */ + template + friend std::basic_istream& + operator>>(std::basic_istream& is, binomial_distribution& bd) + { + bd.read(is); + return is; + } +#endif + + /** Returns true if the two distributions will produce the same + sequence of values, given equal generators. */ + friend bool operator==(const binomial_distribution& lhs, + const binomial_distribution& rhs) + { + return lhs._t == rhs._t && lhs._p == rhs._p; + } + /** Returns true if the two distributions could produce different + sequences of values, given equal generators. */ + friend bool operator!=(const binomial_distribution& lhs, + const binomial_distribution& rhs) + { + return !(lhs == rhs); + } private: - bernoulli_distribution _bernoulli; - IntType _t; + + /// @cond show_private + + template + void read(std::basic_istream& is) { + param_type parm; + if(is >> parm) { + param(parm); + } + } + + bool use_inversion() const + { + // BTRD is safe when np >= 10 + return m < 11; + } + + // computes the correction factor for the Stirling approximation + // for log(k!) + static RealType fc(IntType k) + { + if(k < 10) return detail::binomial_table::table[k]; + else { + RealType ikp1 = RealType(1) / (k + 1); + return (RealType(1)/12 + - (RealType(1)/360 + - (RealType(1)/1260)*(ikp1*ikp1))*(ikp1*ikp1))*ikp1; + } + } + + void init() + { + using std::sqrt; + using std::pow; + + RealType p = (0.5 < _p)? (1 - _p) : _p; + IntType t = _t; + + m = static_cast((t+1)*p); + + if(use_inversion()) { + q_n = pow((1 - p), static_cast(t)); + } else { + btrd.r = p/(1-p); + btrd.nr = (t+1)*btrd.r; + btrd.npq = t*p*(1-p); + RealType sqrt_npq = sqrt(btrd.npq); + btrd.b = 1.15 + 2.53 * sqrt_npq; + btrd.a = -0.0873 + 0.0248*btrd.b + 0.01*p; + btrd.c = t*p + 0.5; + btrd.alpha = (2.83 + 5.1/btrd.b) * sqrt_npq; + btrd.v_r = 0.92 - 4.2/btrd.b; + btrd.u_rv_r = 0.86*btrd.v_r; + } + } + + template + result_type generate(URNG& urng) const + { + using std::floor; + using std::abs; + using std::log; + + while(true) { + RealType u; + RealType v = uniform_01()(urng); + if(v <= btrd.u_rv_r) { + RealType u = v/btrd.v_r - 0.43; + return static_cast(floor( + (2*btrd.a/(0.5 - abs(u)) + btrd.b)*u + btrd.c)); + } + + if(v >= btrd.v_r) { + u = uniform_01()(urng) - 0.5; + } else { + u = v/btrd.v_r - 0.93; + u = ((u < 0)? -0.5 : 0.5) - u; + v = uniform_01()(urng) * btrd.v_r; + } + + RealType us = 0.5 - abs(u); + IntType k = static_cast(floor((2*btrd.a/us + btrd.b)*u + btrd.c)); + if(k < 0 || k > _t) continue; + v = v*btrd.alpha/(btrd.a/(us*us) + btrd.b); + RealType km = abs(k - m); + if(km <= 15) { + RealType f = 1; + if(m < k) { + IntType i = m; + do { + ++i; + f = f*(btrd.nr/i - btrd.r); + } while(i != k); + } else if(m > k) { + IntType i = k; + do { + ++i; + v = v*(btrd.nr/i - btrd.r); + } while(i != m); + } + if(v <= f) return k; + else continue; + } else { + // final acceptance/rejection + v = log(v); + RealType rho = + (km/btrd.npq)*(((km/3. + 0.625)*km + 1./6)/btrd.npq + 0.5); + RealType t = -km*km/(2*btrd.npq); + if(v < t - rho) return k; + if(v > t + rho) continue; + + IntType nm = _t - m + 1; + RealType h = (m + 0.5)*log((m + 1)/(btrd.r*nm)) + + fc(m) + fc(_t - m); + + IntType nk = _t - k + 1; + if(v <= h + (_t+1)*log(static_cast(nm)/nk) + + (k + 0.5)*log(nk*btrd.r/(k+1)) + - fc(k) + - fc(_t - k)) + { + return k; + } else { + continue; + } + } + } + } + + template + IntType invert(IntType t, RealType p, URNG& urng) const + { + RealType q = 1 - p; + RealType s = p / q; + RealType a = (t + 1) * s; + RealType r = q_n; + RealType u = uniform_01()(urng); + IntType x = 0; + while(u > r) { + u = u - r; + ++x; + r = ((a/x) - s) * r; + } + return x; + } + + // parameters + IntType _t; + RealType _p; + + // common data + IntType m; + + union { + // for btrd + struct { + RealType r; + RealType nr; + RealType npq; + RealType b; + RealType a; + RealType c; + RealType alpha; + RealType v_r; + RealType u_rv_r; + } btrd; + // for inversion + RealType q_n; + }; + + /// @endcond }; -} // namespace boost +} + +// backwards compatibility +using random::binomial_distribution; + +} -#endif // BOOST_RANDOM_BINOMIAL_DISTRIBUTION_HPP +#include + +#endif