123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434 |
- /* 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 <boost/config/no_tr1/cmath.hpp>
- #include <cstdlib>
- #include <iosfwd>
- #include <boost/random/detail/config.hpp>
- #include <boost/random/uniform_01.hpp>
- #include <boost/random/detail/disable_warnings.hpp>
- namespace boost {
- namespace random {
- namespace detail {
- template<class RealType>
- struct binomial_table {
- static const RealType table[10];
- };
- template<class RealType>
- const RealType binomial_table<RealType>::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 IntType = int, class RealType = double>
- 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<class CharT, class Traits>
- friend std::basic_ostream<CharT,Traits>&
- operator<<(std::basic_ostream<CharT,Traits>& os,
- const param_type& parm)
- {
- os << parm._p << " " << parm._t;
- return os;
- }
-
- /** Reads the parameters of the distribution from a @c std::istream. */
- template<class CharT, class Traits>
- friend std::basic_istream<CharT,Traits>&
- operator>>(std::basic_istream<CharT,Traits>& 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<class URNG>
- 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<class URNG>
- 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<class CharT, class Traits>
- friend std::basic_ostream<CharT,Traits>&
- operator<<(std::basic_ostream<CharT,Traits>& os,
- const binomial_distribution& bd)
- {
- os << bd.param();
- return os;
- }
-
- /** Reads the parameters of the distribution from a @c std::istream. */
- template<class CharT, class Traits>
- friend std::basic_istream<CharT,Traits>&
- operator>>(std::basic_istream<CharT,Traits>& 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<class CharT, class Traits>
- void read(std::basic_istream<CharT, Traits>& 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<RealType>::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<IntType>((t+1)*p);
- if(use_inversion()) {
- _u.q_n = pow((1 - p), static_cast<RealType>(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<class URNG>
- result_type generate(URNG& urng) const
- {
- using std::floor;
- using std::abs;
- using std::log;
- while(true) {
- RealType u;
- RealType v = uniform_01<RealType>()(urng);
- if(v <= _u.btrd.u_rv_r) {
- u = v/_u.btrd.v_r - 0.43;
- return static_cast<IntType>(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<RealType>()(urng) - 0.5;
- } else {
- u = v/_u.btrd.v_r - 0.93;
- u = ((u < 0)? -0.5 : 0.5) - u;
- v = uniform_01<RealType>()(urng) * _u.btrd.v_r;
- }
- RealType us = 0.5 - abs(u);
- IntType k = static_cast<IntType>(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<RealType>(nm)/nk)
- + (k + 0.5)*log(nk*_u.btrd.r/(k+1))
- - fc(k)
- - fc(_t - k))
- {
- return k;
- } else {
- continue;
- }
- }
- }
- }
- template<class URNG>
- 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<RealType>()(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<RealType>::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 <boost/random/detail/enable_warnings.hpp>
- #endif
|