]> git.donarmstrong.com Git - rsem.git/blobdiff - boost/random/binomial_distribution.hpp
Updated boost to v1.55.0
[rsem.git] / boost / random / binomial_distribution.hpp
index f0e46a50eaa3f01e59565efa506b2cd8dffaead8..dbe2784d2a65e938a9c5e316a86afdb68cbfc62d 100644 (file)
 /* 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 <boost/config/no_tr1/cmath.hpp>
-#include <cassert>
+#include <cstdlib>
+#include <iosfwd>
+
 #include <boost/random/detail/config.hpp>
-#include <boost/random/bernoulli_distribution.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 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 IntType = int, class RealType = double>
-class binomial_distribution
-{
+class binomial_distribution {
 public:
-  typedef typename bernoulli_distribution<RealType>::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<class Engine>
-  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<class CharT, class Traits>
-  friend std::basic_ostream<CharT,Traits>&
-  operator<<(std::basic_ostream<CharT,Traits>& os, const binomial_distribution& bd)
-  {
-    os << bd._bernoulli << " " << bd._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, 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<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:
-  bernoulli_distribution<RealType> _bernoulli;
-  IntType _t;
+
+    /// @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()) {
+            q_n = pow((1 - p), static_cast<RealType>(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<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 <= btrd.u_rv_r) {
+                RealType u = v/btrd.v_r - 0.43;
+                return static_cast<IntType>(floor(
+                    (2*btrd.a/(0.5 - abs(u)) + btrd.b)*u + btrd.c));
+            }
+
+            if(v >= btrd.v_r) {
+                u = uniform_01<RealType>()(urng) - 0.5;
+            } else {
+                u = v/btrd.v_r - 0.93;
+                u = ((u < 0)? -0.5 : 0.5) - u;
+                v = uniform_01<RealType>()(urng) * btrd.v_r;
+            }
+
+            RealType us = 0.5 - abs(u);
+            IntType k = static_cast<IntType>(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<RealType>(nm)/nk)
+                          + (k + 0.5)*log(nk*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 = q_n;
+        RealType u = uniform_01<RealType>()(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 <boost/random/detail/enable_warnings.hpp>
+
+#endif