123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640 |
- /* boost random/discrete_distribution.hpp header file
- *
- * Copyright Steven Watanabe 2009-2011
- * 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_DISCRETE_DISTRIBUTION_HPP_INCLUDED
- #define BOOST_RANDOM_DISCRETE_DISTRIBUTION_HPP_INCLUDED
- #include <vector>
- #include <limits>
- #include <numeric>
- #include <utility>
- #include <iterator>
- #include <boost/assert.hpp>
- #include <boost/random/uniform_01.hpp>
- #include <boost/random/uniform_int_distribution.hpp>
- #include <boost/random/detail/config.hpp>
- #include <boost/random/detail/operators.hpp>
- #include <boost/random/detail/vector_io.hpp>
- #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
- #include <initializer_list>
- #endif
- #include <boost/range/begin.hpp>
- #include <boost/range/end.hpp>
- #include <boost/random/detail/disable_warnings.hpp>
- namespace boost {
- namespace random {
- namespace detail {
- template<class IntType, class WeightType>
- struct integer_alias_table {
- WeightType get_weight(IntType bin) const {
- WeightType result = _average;
- if(bin < _excess) ++result;
- return result;
- }
- template<class Iter>
- WeightType init_average(Iter begin, Iter end) {
- WeightType weight_average = 0;
- IntType excess = 0;
- IntType n = 0;
- // weight_average * n + excess == current partial sum
- // This is a bit messy, but it's guaranteed not to overflow
- for(Iter iter = begin; iter != end; ++iter) {
- ++n;
- if(*iter < weight_average) {
- WeightType diff = weight_average - *iter;
- weight_average -= diff / n;
- if(diff % n > excess) {
- --weight_average;
- excess += n - diff % n;
- } else {
- excess -= diff % n;
- }
- } else {
- WeightType diff = *iter - weight_average;
- weight_average += diff / n;
- if(diff % n < n - excess) {
- excess += diff % n;
- } else {
- ++weight_average;
- excess -= n - diff % n;
- }
- }
- }
- _alias_table.resize(static_cast<std::size_t>(n));
- _average = weight_average;
- _excess = excess;
- return weight_average;
- }
- void init_empty()
- {
- _alias_table.clear();
- _alias_table.push_back(std::make_pair(static_cast<WeightType>(1),
- static_cast<IntType>(0)));
- _average = static_cast<WeightType>(1);
- _excess = static_cast<IntType>(0);
- }
- bool operator==(const integer_alias_table& other) const
- {
- return _alias_table == other._alias_table &&
- _average == other._average && _excess == other._excess;
- }
- static WeightType normalize(WeightType val, WeightType /* average */)
- {
- return val;
- }
- static void normalize(std::vector<WeightType>&) {}
- template<class URNG>
- WeightType test(URNG &urng) const
- {
- return uniform_int_distribution<WeightType>(0, _average)(urng);
- }
- bool accept(IntType result, WeightType val) const
- {
- return result < _excess || val < _average;
- }
- static WeightType try_get_sum(const std::vector<WeightType>& weights)
- {
- WeightType result = static_cast<WeightType>(0);
- for(typename std::vector<WeightType>::const_iterator
- iter = weights.begin(), end = weights.end();
- iter != end; ++iter)
- {
- if((std::numeric_limits<WeightType>::max)() - result > *iter) {
- return static_cast<WeightType>(0);
- }
- result += *iter;
- }
- return result;
- }
- template<class URNG>
- static WeightType generate_in_range(URNG &urng, WeightType max)
- {
- return uniform_int_distribution<WeightType>(
- static_cast<WeightType>(0), max-1)(urng);
- }
- typedef std::vector<std::pair<WeightType, IntType> > alias_table_t;
- alias_table_t _alias_table;
- WeightType _average;
- IntType _excess;
- };
- template<class IntType, class WeightType>
- struct real_alias_table {
- WeightType get_weight(IntType) const
- {
- return WeightType(1.0);
- }
- template<class Iter>
- WeightType init_average(Iter first, Iter last)
- {
- std::size_t size = std::distance(first, last);
- WeightType weight_sum =
- std::accumulate(first, last, static_cast<WeightType>(0));
- _alias_table.resize(size);
- return weight_sum / size;
- }
- void init_empty()
- {
- _alias_table.clear();
- _alias_table.push_back(std::make_pair(static_cast<WeightType>(1),
- static_cast<IntType>(0)));
- }
- bool operator==(const real_alias_table& other) const
- {
- return _alias_table == other._alias_table;
- }
- static WeightType normalize(WeightType val, WeightType average)
- {
- return val / average;
- }
- static void normalize(std::vector<WeightType>& weights)
- {
- WeightType sum =
- std::accumulate(weights.begin(), weights.end(),
- static_cast<WeightType>(0));
- for(typename std::vector<WeightType>::iterator
- iter = weights.begin(),
- end = weights.end();
- iter != end; ++iter)
- {
- *iter /= sum;
- }
- }
- template<class URNG>
- WeightType test(URNG &urng) const
- {
- return uniform_01<WeightType>()(urng);
- }
- bool accept(IntType, WeightType) const
- {
- return true;
- }
- static WeightType try_get_sum(const std::vector<WeightType>& /* weights */)
- {
- return static_cast<WeightType>(1);
- }
- template<class URNG>
- static WeightType generate_in_range(URNG &urng, WeightType)
- {
- return uniform_01<WeightType>()(urng);
- }
- typedef std::vector<std::pair<WeightType, IntType> > alias_table_t;
- alias_table_t _alias_table;
- };
- template<bool IsIntegral>
- struct select_alias_table;
- template<>
- struct select_alias_table<true> {
- template<class IntType, class WeightType>
- struct apply {
- typedef integer_alias_table<IntType, WeightType> type;
- };
- };
- template<>
- struct select_alias_table<false> {
- template<class IntType, class WeightType>
- struct apply {
- typedef real_alias_table<IntType, WeightType> type;
- };
- };
- }
- /**
- * The class @c discrete_distribution models a \random_distribution.
- * It produces integers in the range [0, n) with the probability
- * of producing each value is specified by the parameters of the
- * distribution.
- */
- template<class IntType = int, class WeightType = double>
- class discrete_distribution {
- public:
- typedef WeightType input_type;
- typedef IntType result_type;
- class param_type {
- public:
- typedef discrete_distribution distribution_type;
- /**
- * Constructs a @c param_type object, representing a distribution
- * with \f$p(0) = 1\f$ and \f$p(k|k>0) = 0\f$.
- */
- param_type() : _probabilities(1, static_cast<WeightType>(1)) {}
- /**
- * If @c first == @c last, equivalent to the default constructor.
- * Otherwise, the values of the range represent weights for the
- * possible values of the distribution.
- */
- template<class Iter>
- param_type(Iter first, Iter last) : _probabilities(first, last)
- {
- normalize();
- }
- #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
- /**
- * If wl.size() == 0, equivalent to the default constructor.
- * Otherwise, the values of the @c initializer_list represent
- * weights for the possible values of the distribution.
- */
- param_type(const std::initializer_list<WeightType>& wl)
- : _probabilities(wl)
- {
- normalize();
- }
- #endif
- /**
- * If the range is empty, equivalent to the default constructor.
- * Otherwise, the elements of the range represent
- * weights for the possible values of the distribution.
- */
- template<class Range>
- explicit param_type(const Range& range)
- : _probabilities(boost::begin(range), boost::end(range))
- {
- normalize();
- }
- /**
- * If nw is zero, equivalent to the default constructor.
- * Otherwise, the range of the distribution is [0, nw),
- * and the weights are found by calling fw with values
- * evenly distributed between \f$\mbox{xmin} + \delta/2\f$ and
- * \f$\mbox{xmax} - \delta/2\f$, where
- * \f$\delta = (\mbox{xmax} - \mbox{xmin})/\mbox{nw}\f$.
- */
- template<class Func>
- param_type(std::size_t nw, double xmin, double xmax, Func fw)
- {
- std::size_t n = (nw == 0) ? 1 : nw;
- double delta = (xmax - xmin) / n;
- BOOST_ASSERT(delta > 0);
- for(std::size_t k = 0; k < n; ++k) {
- _probabilities.push_back(fw(xmin + k*delta + delta/2));
- }
- normalize();
- }
- /**
- * Returns a vector containing the probabilities of each possible
- * value of the distribution.
- */
- std::vector<WeightType> probabilities() const
- {
- return _probabilities;
- }
- /** Writes the parameters to a @c std::ostream. */
- BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
- {
- detail::print_vector(os, parm._probabilities);
- return os;
- }
-
- /** Reads the parameters from a @c std::istream. */
- BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
- {
- std::vector<WeightType> temp;
- detail::read_vector(is, temp);
- if(is) {
- parm._probabilities.swap(temp);
- }
- return is;
- }
- /** Returns true if the two sets of parameters are the same. */
- BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
- {
- return lhs._probabilities == rhs._probabilities;
- }
- /** Returns true if the two sets of parameters are different. */
- BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
- private:
- /// @cond show_private
- friend class discrete_distribution;
- explicit param_type(const discrete_distribution& dist)
- : _probabilities(dist.probabilities())
- {}
- void normalize()
- {
- impl_type::normalize(_probabilities);
- }
- std::vector<WeightType> _probabilities;
- /// @endcond
- };
- /**
- * Creates a new @c discrete_distribution object that has
- * \f$p(0) = 1\f$ and \f$p(i|i>0) = 0\f$.
- */
- discrete_distribution()
- {
- _impl.init_empty();
- }
- /**
- * Constructs a discrete_distribution from an iterator range.
- * If @c first == @c last, equivalent to the default constructor.
- * Otherwise, the values of the range represent weights for the
- * possible values of the distribution.
- */
- template<class Iter>
- discrete_distribution(Iter first, Iter last)
- {
- init(first, last);
- }
- #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
- /**
- * Constructs a @c discrete_distribution from a @c std::initializer_list.
- * If the @c initializer_list is empty, equivalent to the default
- * constructor. Otherwise, the values of the @c initializer_list
- * represent weights for the possible values of the distribution.
- * For example, given the distribution
- *
- * @code
- * discrete_distribution<> dist{1, 4, 5};
- * @endcode
- *
- * The probability of a 0 is 1/10, the probability of a 1 is 2/5,
- * the probability of a 2 is 1/2, and no other values are possible.
- */
- discrete_distribution(std::initializer_list<WeightType> wl)
- {
- init(wl.begin(), wl.end());
- }
- #endif
- /**
- * Constructs a discrete_distribution from a Boost.Range range.
- * If the range is empty, equivalent to the default constructor.
- * Otherwise, the values of the range represent weights for the
- * possible values of the distribution.
- */
- template<class Range>
- explicit discrete_distribution(const Range& range)
- {
- init(boost::begin(range), boost::end(range));
- }
- /**
- * Constructs a discrete_distribution that approximates a function.
- * If nw is zero, equivalent to the default constructor.
- * Otherwise, the range of the distribution is [0, nw),
- * and the weights are found by calling fw with values
- * evenly distributed between \f$\mbox{xmin} + \delta/2\f$ and
- * \f$\mbox{xmax} - \delta/2\f$, where
- * \f$\delta = (\mbox{xmax} - \mbox{xmin})/\mbox{nw}\f$.
- */
- template<class Func>
- discrete_distribution(std::size_t nw, double xmin, double xmax, Func fw)
- {
- std::size_t n = (nw == 0) ? 1 : nw;
- double delta = (xmax - xmin) / n;
- BOOST_ASSERT(delta > 0);
- std::vector<WeightType> weights;
- for(std::size_t k = 0; k < n; ++k) {
- weights.push_back(fw(xmin + k*delta + delta/2));
- }
- init(weights.begin(), weights.end());
- }
- /**
- * Constructs a discrete_distribution from its parameters.
- */
- explicit discrete_distribution(const param_type& parm)
- {
- param(parm);
- }
- /**
- * Returns a value distributed according to the parameters of the
- * discrete_distribution.
- */
- template<class URNG>
- IntType operator()(URNG& urng) const
- {
- BOOST_ASSERT(!_impl._alias_table.empty());
- IntType result;
- WeightType test;
- do {
- result = uniform_int_distribution<IntType>((min)(), (max)())(urng);
- test = _impl.test(urng);
- } while(!_impl.accept(result, test));
- if(test < _impl._alias_table[static_cast<std::size_t>(result)].first) {
- return result;
- } else {
- return(_impl._alias_table[static_cast<std::size_t>(result)].second);
- }
- }
-
- /**
- * Returns a value distributed according to the parameters
- * specified by param.
- */
- template<class URNG>
- IntType operator()(URNG& urng, const param_type& parm) const
- {
- if(WeightType limit = impl_type::try_get_sum(parm._probabilities)) {
- WeightType val = impl_type::generate_in_range(urng, limit);
- WeightType sum = 0;
- std::size_t result = 0;
- for(typename std::vector<WeightType>::const_iterator
- iter = parm._probabilities.begin(),
- end = parm._probabilities.end();
- iter != end; ++iter, ++result)
- {
- sum += *iter;
- if(sum > val) {
- return result;
- }
- }
- // This shouldn't be reachable, but round-off error
- // can prevent any match from being found when val is
- // very close to 1.
- return static_cast<IntType>(parm._probabilities.size() - 1);
- } else {
- // WeightType is integral and sum(parm._probabilities)
- // would overflow. Just use the easy solution.
- return discrete_distribution(parm)(urng);
- }
- }
-
- /** Returns the smallest value that the distribution can produce. */
- result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 0; }
- /** Returns the largest value that the distribution can produce. */
- result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
- { return static_cast<result_type>(_impl._alias_table.size() - 1); }
- /**
- * Returns a vector containing the probabilities of each
- * value of the distribution. For example, given
- *
- * @code
- * discrete_distribution<> dist = { 1, 4, 5 };
- * std::vector<double> p = dist.param();
- * @endcode
- *
- * the vector, p will contain {0.1, 0.4, 0.5}.
- *
- * If @c WeightType is integral, then the weights
- * will be returned unchanged.
- */
- std::vector<WeightType> probabilities() const
- {
- std::vector<WeightType> result(_impl._alias_table.size(), static_cast<WeightType>(0));
- std::size_t i = 0;
- for(typename impl_type::alias_table_t::const_iterator
- iter = _impl._alias_table.begin(),
- end = _impl._alias_table.end();
- iter != end; ++iter, ++i)
- {
- WeightType val = iter->first;
- result[i] += val;
- result[static_cast<std::size_t>(iter->second)] += _impl.get_weight(i) - val;
- }
- impl_type::normalize(result);
- return(result);
- }
- /** Returns the parameters of the distribution. */
- param_type param() const
- {
- return param_type(*this);
- }
- /** Sets the parameters of the distribution. */
- void param(const param_type& parm)
- {
- init(parm._probabilities.begin(), parm._probabilities.end());
- }
-
- /**
- * Effects: Subsequent uses of the distribution do not depend
- * on values produced by any engine prior to invoking reset.
- */
- void reset() {}
- /** Writes a distribution to a @c std::ostream. */
- BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, discrete_distribution, dd)
- {
- os << dd.param();
- return os;
- }
- /** Reads a distribution from a @c std::istream */
- BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, discrete_distribution, dd)
- {
- param_type parm;
- if(is >> parm) {
- dd.param(parm);
- }
- return is;
- }
- /**
- * Returns true if the two distributions will return the
- * same sequence of values, when passed equal generators.
- */
- BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(discrete_distribution, lhs, rhs)
- {
- return lhs._impl == rhs._impl;
- }
- /**
- * Returns true if the two distributions may return different
- * sequences of values, when passed equal generators.
- */
- BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(discrete_distribution)
- private:
- /// @cond show_private
- template<class Iter>
- void init(Iter first, Iter last, std::input_iterator_tag)
- {
- std::vector<WeightType> temp(first, last);
- init(temp.begin(), temp.end());
- }
- template<class Iter>
- void init(Iter first, Iter last, std::forward_iterator_tag)
- {
- size_t input_size = std::distance(first, last);
- std::vector<std::pair<WeightType, IntType> > below_average;
- std::vector<std::pair<WeightType, IntType> > above_average;
- below_average.reserve(input_size);
- above_average.reserve(input_size);
-
- WeightType weight_average = _impl.init_average(first, last);
- WeightType normalized_average = _impl.get_weight(0);
- std::size_t i = 0;
- for(; first != last; ++first, ++i) {
- WeightType val = impl_type::normalize(*first, weight_average);
- std::pair<WeightType, IntType> elem(val, static_cast<IntType>(i));
- if(val < normalized_average) {
- below_average.push_back(elem);
- } else {
- above_average.push_back(elem);
- }
- }
- typename impl_type::alias_table_t::iterator
- b_iter = below_average.begin(),
- b_end = below_average.end(),
- a_iter = above_average.begin(),
- a_end = above_average.end()
- ;
- while(b_iter != b_end && a_iter != a_end) {
- _impl._alias_table[static_cast<std::size_t>(b_iter->second)] =
- std::make_pair(b_iter->first, a_iter->second);
- a_iter->first -= (_impl.get_weight(b_iter->second) - b_iter->first);
- if(a_iter->first < normalized_average) {
- *b_iter = *a_iter++;
- } else {
- ++b_iter;
- }
- }
- for(; b_iter != b_end; ++b_iter) {
- _impl._alias_table[static_cast<std::size_t>(b_iter->second)].first =
- _impl.get_weight(b_iter->second);
- }
- for(; a_iter != a_end; ++a_iter) {
- _impl._alias_table[static_cast<std::size_t>(a_iter->second)].first =
- _impl.get_weight(a_iter->second);
- }
- }
- template<class Iter>
- void init(Iter first, Iter last)
- {
- if(first == last) {
- _impl.init_empty();
- } else {
- typename std::iterator_traits<Iter>::iterator_category category;
- init(first, last, category);
- }
- }
- typedef typename detail::select_alias_table<
- (::boost::is_integral<WeightType>::value)
- >::template apply<IntType, WeightType>::type impl_type;
- impl_type _impl;
- /// @endcond
- };
- }
- }
- #include <boost/random/detail/enable_warnings.hpp>
- #endif
|