/* boost random/binomial_distribution.hpp header file * * 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$ */ #ifndef BOOST_RANDOM_BINOMIAL_DISTRIBUTION_HPP_INCLUDED #define BOOST_RANDOM_BINOMIAL_DISTRIBUTION_HPP_INCLUDED #include #include #include #include #include #include namespace lslboost { 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 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 { public: 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 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: /// @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()) { _u.q_n = pow((1 - p), static_cast(t)); } else { _u.btrd.r = p/(1-p); _u.btrd.nr = (t+1)*_u.btrd.r; _u.btrd.npq = t*p*(1-p); RealType sqrt_npq = sqrt(_u.btrd.npq); _u.btrd.b = 1.15 + 2.53 * sqrt_npq; _u.btrd.a = -0.0873 + 0.0248*_u.btrd.b + 0.01*p; _u.btrd.c = t*p + 0.5; _u.btrd.alpha = (2.83 + 5.1/_u.btrd.b) * sqrt_npq; _u.btrd.v_r = 0.92 - 4.2/_u.btrd.b; _u.btrd.u_rv_r = 0.86*_u.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 <= _u.btrd.u_rv_r) { u = v/_u.btrd.v_r - 0.43; return static_cast(floor( (2*_u.btrd.a/(0.5 - abs(u)) + _u.btrd.b)*u + _u.btrd.c)); } if(v >= _u.btrd.v_r) { u = uniform_01()(urng) - 0.5; } else { u = v/_u.btrd.v_r - 0.93; u = ((u < 0)? -0.5 : 0.5) - u; v = uniform_01()(urng) * _u.btrd.v_r; } RealType us = 0.5 - abs(u); IntType k = static_cast(floor((2*_u.btrd.a/us + _u.btrd.b)*u + _u.btrd.c)); if(k < 0 || k > _t) continue; v = v*_u.btrd.alpha/(_u.btrd.a/(us*us) + _u.btrd.b); RealType km = abs(k - m); if(km <= 15) { RealType f = 1; if(m < k) { IntType i = m; do { ++i; f = f*(_u.btrd.nr/i - _u.btrd.r); } while(i != k); } else if(m > k) { IntType i = k; do { ++i; v = v*(_u.btrd.nr/i - _u.btrd.r); } while(i != m); } if(v <= f) return k; else continue; } else { // final acceptance/rejection v = log(v); RealType rho = (km/_u.btrd.npq)*(((km/3. + 0.625)*km + 1./6)/_u.btrd.npq + 0.5); RealType t = -km*km/(2*_u.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)/(_u.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*_u.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 = _u.q_n; RealType u = uniform_01()(urng); IntType x = 0; while(u > r) { u = u - r; ++x; RealType r1 = ((a/x) - s) * r; // If r gets too small then the round-off error // becomes a problem. At this point, p(i) is // decreasing exponentially, so if we just call // it 0, it's close enough. Note that the // minimum value of q_n is about 1e-7, so we // may need to be a little careful to make sure that // we don't terminate the first time through the loop // for float. (Hence the test that r is decreasing) if(r1 < std::numeric_limits::epsilon() && r1 < r) { break; } r = r1; } 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; } _u; /// @endcond }; } // backwards compatibility using random::binomial_distribution; } #include #endif