Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-30 09:59:11

0001 /* boost random/discrete_distribution.hpp header file
0002  *
0003  * Copyright Steven Watanabe 2009-2011
0004  * Distributed under the Boost Software License, Version 1.0. (See
0005  * accompanying file LICENSE_1_0.txt or copy at
0006  * http://www.boost.org/LICENSE_1_0.txt)
0007  *
0008  * See http://www.boost.org for most recent version including documentation.
0009  *
0010  * $Id$
0011  */
0012 
0013 #ifndef BOOST_RANDOM_DISCRETE_DISTRIBUTION_HPP_INCLUDED
0014 #define BOOST_RANDOM_DISCRETE_DISTRIBUTION_HPP_INCLUDED
0015 
0016 #include <vector>
0017 #include <limits>
0018 #include <numeric>
0019 #include <utility>
0020 #include <iterator>
0021 #include <boost/assert.hpp>
0022 #include <boost/random/uniform_01.hpp>
0023 #include <boost/random/uniform_int_distribution.hpp>
0024 #include <boost/random/detail/config.hpp>
0025 #include <boost/random/detail/operators.hpp>
0026 #include <boost/random/detail/vector_io.hpp>
0027 
0028 #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
0029 #include <initializer_list>
0030 #endif
0031 
0032 #include <boost/range/begin.hpp>
0033 #include <boost/range/end.hpp>
0034 
0035 #include <boost/random/detail/disable_warnings.hpp>
0036 
0037 namespace boost {
0038 namespace random {
0039 namespace detail {
0040 
0041 template<class IntType, class WeightType>
0042 struct integer_alias_table {
0043     WeightType get_weight(IntType bin) const {
0044         WeightType result = _average;
0045         if(bin < _excess) ++result;
0046         return result;
0047     }
0048     template<class Iter>
0049     WeightType init_average(Iter begin, Iter end) {
0050         WeightType weight_average = 0;
0051         IntType excess = 0;
0052         IntType n = 0;
0053         // weight_average * n + excess == current partial sum
0054         // This is a bit messy, but it's guaranteed not to overflow
0055         for(Iter iter = begin; iter != end; ++iter) {
0056             ++n;
0057             if(*iter < weight_average) {
0058                 WeightType diff = weight_average - *iter;
0059                 weight_average -= diff / n;
0060                 if(diff % n > excess) {
0061                     --weight_average;
0062                     excess += n - diff % n;
0063                 } else {
0064                     excess -= diff % n;
0065                 }
0066             } else {
0067                 WeightType diff = *iter - weight_average;
0068                 weight_average += diff / n;
0069                 if(diff % n < n - excess) {
0070                     excess += diff % n;
0071                 } else {
0072                     ++weight_average;
0073                     excess -= n - diff % n;
0074                 }
0075             }
0076         }
0077         _alias_table.resize(static_cast<std::size_t>(n));
0078         _average = weight_average;
0079         _excess = excess;
0080         return weight_average;
0081     }
0082     void init_empty()
0083     {
0084         _alias_table.clear();
0085         _alias_table.push_back(std::make_pair(static_cast<WeightType>(1),
0086                                               static_cast<IntType>(0)));
0087         _average = static_cast<WeightType>(1);
0088         _excess = static_cast<IntType>(0);
0089     }
0090     bool operator==(const integer_alias_table& other) const
0091     {
0092         return _alias_table == other._alias_table &&
0093             _average == other._average && _excess == other._excess;
0094     }
0095     static WeightType normalize(WeightType val, WeightType /* average */)
0096     {
0097         return val;
0098     }
0099     static void normalize(std::vector<WeightType>&) {}
0100     template<class URNG>
0101     WeightType test(URNG &urng) const
0102     {
0103         return uniform_int_distribution<WeightType>(0, _average)(urng);
0104     }
0105     bool accept(IntType result, WeightType val) const
0106     {
0107         return result < _excess || val < _average;
0108     }
0109     static WeightType try_get_sum(const std::vector<WeightType>& weights)
0110     {
0111         WeightType result = static_cast<WeightType>(0);
0112         for(typename std::vector<WeightType>::const_iterator
0113                 iter = weights.begin(), end = weights.end();
0114             iter != end; ++iter)
0115         {
0116             if((std::numeric_limits<WeightType>::max)() - result > *iter) {
0117                 return static_cast<WeightType>(0);
0118             }
0119             result += *iter;
0120         }
0121         return result;
0122     }
0123     template<class URNG>
0124     static WeightType generate_in_range(URNG &urng, WeightType max)
0125     {
0126         return uniform_int_distribution<WeightType>(
0127             static_cast<WeightType>(0), max-1)(urng);
0128     }
0129     typedef std::vector<std::pair<WeightType, IntType> > alias_table_t;
0130     alias_table_t _alias_table;
0131     WeightType _average;
0132     IntType _excess;
0133 };
0134 
0135 template<class IntType, class WeightType>
0136 struct real_alias_table {
0137     WeightType get_weight(IntType) const
0138     {
0139         return WeightType(1.0);
0140     }
0141     template<class Iter>
0142     WeightType init_average(Iter first, Iter last)
0143     {
0144         std::size_t size = std::distance(first, last);
0145         WeightType weight_sum =
0146             std::accumulate(first, last, static_cast<WeightType>(0));
0147         _alias_table.resize(size);
0148         return weight_sum / size;
0149     }
0150     void init_empty()
0151     {
0152         _alias_table.clear();
0153         _alias_table.push_back(std::make_pair(static_cast<WeightType>(1),
0154                                               static_cast<IntType>(0)));
0155     }
0156     bool operator==(const real_alias_table& other) const
0157     {
0158         return _alias_table == other._alias_table;
0159     }
0160     static WeightType normalize(WeightType val, WeightType average)
0161     {
0162         return val / average;
0163     }
0164     static void normalize(std::vector<WeightType>& weights)
0165     {
0166         WeightType sum =
0167             std::accumulate(weights.begin(), weights.end(),
0168                             static_cast<WeightType>(0));
0169         for(typename std::vector<WeightType>::iterator
0170                 iter = weights.begin(),
0171                 end = weights.end();
0172             iter != end; ++iter)
0173         {
0174             *iter /= sum;
0175         }
0176     }
0177     template<class URNG>
0178     WeightType test(URNG &urng) const
0179     {
0180         return uniform_01<WeightType>()(urng);
0181     }
0182     bool accept(IntType, WeightType) const
0183     {
0184         return true;
0185     }
0186     static WeightType try_get_sum(const std::vector<WeightType>& /* weights */)
0187     {
0188         return static_cast<WeightType>(1);
0189     }
0190     template<class URNG>
0191     static WeightType generate_in_range(URNG &urng, WeightType)
0192     {
0193         return uniform_01<WeightType>()(urng);
0194     }
0195     typedef std::vector<std::pair<WeightType, IntType> > alias_table_t;
0196     alias_table_t _alias_table;
0197 };
0198 
0199 template<bool IsIntegral>
0200 struct select_alias_table;
0201 
0202 template<>
0203 struct select_alias_table<true> {
0204     template<class IntType, class WeightType>
0205     struct apply {
0206         typedef integer_alias_table<IntType, WeightType> type;
0207     };
0208 };
0209 
0210 template<>
0211 struct select_alias_table<false> {
0212     template<class IntType, class WeightType>
0213     struct apply {
0214         typedef real_alias_table<IntType, WeightType> type;
0215     };
0216 };
0217 
0218 }
0219 
0220 /**
0221  * The class @c discrete_distribution models a \random_distribution.
0222  * It produces integers in the range [0, n) with the probability
0223  * of producing each value is specified by the parameters of the
0224  * distribution.
0225  */
0226 template<class IntType = int, class WeightType = double>
0227 class discrete_distribution {
0228 public:
0229     typedef WeightType input_type;
0230     typedef IntType result_type;
0231 
0232     class param_type {
0233     public:
0234 
0235         typedef discrete_distribution distribution_type;
0236 
0237         /**
0238          * Constructs a @c param_type object, representing a distribution
0239          * with \f$p(0) = 1\f$ and \f$p(k|k>0) = 0\f$.
0240          */
0241         param_type() : _probabilities(1, static_cast<WeightType>(1)) {}
0242         /**
0243          * If @c first == @c last, equivalent to the default constructor.
0244          * Otherwise, the values of the range represent weights for the
0245          * possible values of the distribution.
0246          */
0247         template<class Iter>
0248         param_type(Iter first, Iter last) : _probabilities(first, last)
0249         {
0250             normalize();
0251         }
0252 #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
0253         /**
0254          * If wl.size() == 0, equivalent to the default constructor.
0255          * Otherwise, the values of the @c initializer_list represent
0256          * weights for the possible values of the distribution.
0257          */
0258         param_type(const std::initializer_list<WeightType>& wl)
0259           : _probabilities(wl)
0260         {
0261             normalize();
0262         }
0263 #endif
0264         /**
0265          * If the range is empty, equivalent to the default constructor.
0266          * Otherwise, the elements of the range represent
0267          * weights for the possible values of the distribution.
0268          */
0269         template<class Range>
0270         explicit param_type(const Range& range)
0271           : _probabilities(boost::begin(range), boost::end(range))
0272         {
0273             normalize();
0274         }
0275 
0276         /**
0277          * If nw is zero, equivalent to the default constructor.
0278          * Otherwise, the range of the distribution is [0, nw),
0279          * and the weights are found by  calling fw with values
0280          * evenly distributed between \f$\mbox{xmin} + \delta/2\f$ and
0281          * \f$\mbox{xmax} - \delta/2\f$, where
0282          * \f$\delta = (\mbox{xmax} - \mbox{xmin})/\mbox{nw}\f$.
0283          */
0284         template<class Func>
0285         param_type(std::size_t nw, double xmin, double xmax, Func fw)
0286         {
0287             std::size_t n = (nw == 0) ? 1 : nw;
0288             double delta = (xmax - xmin) / n;
0289             BOOST_ASSERT(delta > 0);
0290             for(std::size_t k = 0; k < n; ++k) {
0291                 _probabilities.push_back(fw(xmin + k*delta + delta/2));
0292             }
0293             normalize();
0294         }
0295 
0296         /**
0297          * Returns a vector containing the probabilities of each possible
0298          * value of the distribution.
0299          */
0300         std::vector<WeightType> probabilities() const
0301         {
0302             return _probabilities;
0303         }
0304 
0305         /** Writes the parameters to a @c std::ostream. */
0306         BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
0307         {
0308             detail::print_vector(os, parm._probabilities);
0309             return os;
0310         }
0311         
0312         /** Reads the parameters from a @c std::istream. */
0313         BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
0314         {
0315             std::vector<WeightType> temp;
0316             detail::read_vector(is, temp);
0317             if(is) {
0318                 parm._probabilities.swap(temp);
0319             }
0320             return is;
0321         }
0322 
0323         /** Returns true if the two sets of parameters are the same. */
0324         BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
0325         {
0326             return lhs._probabilities == rhs._probabilities;
0327         }
0328         /** Returns true if the two sets of parameters are different. */
0329         BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
0330     private:
0331         /// @cond show_private
0332         friend class discrete_distribution;
0333         explicit param_type(const discrete_distribution& dist)
0334           : _probabilities(dist.probabilities())
0335         {}
0336         void normalize()
0337         {
0338             impl_type::normalize(_probabilities);
0339         }
0340         std::vector<WeightType> _probabilities;
0341         /// @endcond
0342     };
0343 
0344     /**
0345      * Creates a new @c discrete_distribution object that has
0346      * \f$p(0) = 1\f$ and \f$p(i|i>0) = 0\f$.
0347      */
0348     discrete_distribution()
0349     {
0350         _impl.init_empty();
0351     }
0352     /**
0353      * Constructs a discrete_distribution from an iterator range.
0354      * If @c first == @c last, equivalent to the default constructor.
0355      * Otherwise, the values of the range represent weights for the
0356      * possible values of the distribution.
0357      */
0358     template<class Iter>
0359     discrete_distribution(Iter first, Iter last)
0360     {
0361         init(first, last);
0362     }
0363 #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
0364     /**
0365      * Constructs a @c discrete_distribution from a @c std::initializer_list.
0366      * If the @c initializer_list is empty, equivalent to the default
0367      * constructor.  Otherwise, the values of the @c initializer_list
0368      * represent weights for the possible values of the distribution.
0369      * For example, given the distribution
0370      *
0371      * @code
0372      * discrete_distribution<> dist{1, 4, 5};
0373      * @endcode
0374      *
0375      * The probability of a 0 is 1/10, the probability of a 1 is 2/5,
0376      * the probability of a 2 is 1/2, and no other values are possible.
0377      */
0378     discrete_distribution(std::initializer_list<WeightType> wl)
0379     {
0380         init(wl.begin(), wl.end());
0381     }
0382 #endif
0383     /**
0384      * Constructs a discrete_distribution from a Boost.Range range.
0385      * If the range is empty, equivalent to the default constructor.
0386      * Otherwise, the values of the range represent weights for the
0387      * possible values of the distribution.
0388      */
0389     template<class Range>
0390     explicit discrete_distribution(const Range& range)
0391     {
0392         init(boost::begin(range), boost::end(range));
0393     }
0394     /**
0395      * Constructs a discrete_distribution that approximates a function.
0396      * If nw is zero, equivalent to the default constructor.
0397      * Otherwise, the range of the distribution is [0, nw),
0398      * and the weights are found by  calling fw with values
0399      * evenly distributed between \f$\mbox{xmin} + \delta/2\f$ and
0400      * \f$\mbox{xmax} - \delta/2\f$, where
0401      * \f$\delta = (\mbox{xmax} - \mbox{xmin})/\mbox{nw}\f$.
0402      */
0403     template<class Func>
0404     discrete_distribution(std::size_t nw, double xmin, double xmax, Func fw)
0405     {
0406         std::size_t n = (nw == 0) ? 1 : nw;
0407         double delta = (xmax - xmin) / n;
0408         BOOST_ASSERT(delta > 0);
0409         std::vector<WeightType> weights;
0410         for(std::size_t k = 0; k < n; ++k) {
0411             weights.push_back(fw(xmin + k*delta + delta/2));
0412         }
0413         init(weights.begin(), weights.end());
0414     }
0415     /**
0416      * Constructs a discrete_distribution from its parameters.
0417      */
0418     explicit discrete_distribution(const param_type& parm)
0419     {
0420         param(parm);
0421     }
0422 
0423     /**
0424      * Returns a value distributed according to the parameters of the
0425      * discrete_distribution.
0426      */
0427     template<class URNG>
0428     IntType operator()(URNG& urng) const
0429     {
0430         BOOST_ASSERT(!_impl._alias_table.empty());
0431         IntType result;
0432         WeightType test;
0433         do {
0434             result = uniform_int_distribution<IntType>((min)(), (max)())(urng);
0435             test = _impl.test(urng);
0436         } while(!_impl.accept(result, test));
0437         if(test < _impl._alias_table[static_cast<std::size_t>(result)].first) {
0438             return result;
0439         } else {
0440             return(_impl._alias_table[static_cast<std::size_t>(result)].second);
0441         }
0442     }
0443     
0444     /**
0445      * Returns a value distributed according to the parameters
0446      * specified by param.
0447      */
0448     template<class URNG>
0449     IntType operator()(URNG& urng, const param_type& parm) const
0450     {
0451         if(WeightType limit = impl_type::try_get_sum(parm._probabilities)) {
0452             WeightType val = impl_type::generate_in_range(urng, limit);
0453             WeightType sum = 0;
0454             std::size_t result = 0;
0455             for(typename std::vector<WeightType>::const_iterator
0456                     iter = parm._probabilities.begin(),
0457                     end = parm._probabilities.end();
0458                 iter != end; ++iter, ++result)
0459             {
0460                 sum += *iter;
0461                 if(sum > val) {
0462                     return result;
0463                 }
0464             }
0465             // This shouldn't be reachable, but round-off error
0466             // can prevent any match from being found when val is
0467             // very close to 1.
0468             return static_cast<IntType>(parm._probabilities.size() - 1);
0469         } else {
0470             // WeightType is integral and sum(parm._probabilities)
0471             // would overflow.  Just use the easy solution.
0472             return discrete_distribution(parm)(urng);
0473         }
0474     }
0475     
0476     /** Returns the smallest value that the distribution can produce. */
0477     result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 0; }
0478     /** Returns the largest value that the distribution can produce. */
0479     result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
0480     { return static_cast<result_type>(_impl._alias_table.size() - 1); }
0481 
0482     /**
0483      * Returns a vector containing the probabilities of each
0484      * value of the distribution.  For example, given
0485      *
0486      * @code
0487      * discrete_distribution<> dist = { 1, 4, 5 };
0488      * std::vector<double> p = dist.param();
0489      * @endcode
0490      *
0491      * the vector, p will contain {0.1, 0.4, 0.5}.
0492      *
0493      * If @c WeightType is integral, then the weights
0494      * will be returned unchanged.
0495      */
0496     std::vector<WeightType> probabilities() const
0497     {
0498         std::vector<WeightType> result(_impl._alias_table.size(), static_cast<WeightType>(0));
0499         std::size_t i = 0;
0500         for(typename impl_type::alias_table_t::const_iterator
0501                 iter = _impl._alias_table.begin(),
0502                 end = _impl._alias_table.end();
0503                 iter != end; ++iter, ++i)
0504         {
0505             WeightType val = iter->first;
0506             result[i] += val;
0507             result[static_cast<std::size_t>(iter->second)] += _impl.get_weight(i) - val;
0508         }
0509         impl_type::normalize(result);
0510         return(result);
0511     }
0512 
0513     /** Returns the parameters of the distribution. */
0514     param_type param() const
0515     {
0516         return param_type(*this);
0517     }
0518     /** Sets the parameters of the distribution. */
0519     void param(const param_type& parm)
0520     {
0521         init(parm._probabilities.begin(), parm._probabilities.end());
0522     }
0523     
0524     /**
0525      * Effects: Subsequent uses of the distribution do not depend
0526      * on values produced by any engine prior to invoking reset.
0527      */
0528     void reset() {}
0529 
0530     /** Writes a distribution to a @c std::ostream. */
0531     BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, discrete_distribution, dd)
0532     {
0533         os << dd.param();
0534         return os;
0535     }
0536 
0537     /** Reads a distribution from a @c std::istream */
0538     BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, discrete_distribution, dd)
0539     {
0540         param_type parm;
0541         if(is >> parm) {
0542             dd.param(parm);
0543         }
0544         return is;
0545     }
0546 
0547     /**
0548      * Returns true if the two distributions will return the
0549      * same sequence of values, when passed equal generators.
0550      */
0551     BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(discrete_distribution, lhs, rhs)
0552     {
0553         return lhs._impl == rhs._impl;
0554     }
0555     /**
0556      * Returns true if the two distributions may return different
0557      * sequences of values, when passed equal generators.
0558      */
0559     BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(discrete_distribution)
0560 
0561 private:
0562 
0563     /// @cond show_private
0564 
0565     template<class Iter>
0566     void init(Iter first, Iter last, std::input_iterator_tag)
0567     {
0568         std::vector<WeightType> temp(first, last);
0569         init(temp.begin(), temp.end());
0570     }
0571     template<class Iter>
0572     void init(Iter first, Iter last, std::forward_iterator_tag)
0573     {
0574         size_t input_size = std::distance(first, last);
0575         std::vector<std::pair<WeightType, IntType> > below_average;
0576         std::vector<std::pair<WeightType, IntType> > above_average;
0577         below_average.reserve(input_size);
0578         above_average.reserve(input_size);
0579         
0580         WeightType weight_average = _impl.init_average(first, last);
0581         WeightType normalized_average = _impl.get_weight(0);
0582         std::size_t i = 0;
0583         for(; first != last; ++first, ++i) {
0584             WeightType val = impl_type::normalize(*first, weight_average);
0585             std::pair<WeightType, IntType> elem(val, static_cast<IntType>(i));
0586             if(val < normalized_average) {
0587                 below_average.push_back(elem);
0588             } else {
0589                 above_average.push_back(elem);
0590             }
0591         }
0592 
0593         typename impl_type::alias_table_t::iterator
0594             b_iter = below_average.begin(),
0595             b_end = below_average.end(),
0596             a_iter = above_average.begin(),
0597             a_end = above_average.end()
0598             ;
0599         while(b_iter != b_end && a_iter != a_end) {
0600             _impl._alias_table[static_cast<std::size_t>(b_iter->second)] =
0601                 std::make_pair(b_iter->first, a_iter->second);
0602             a_iter->first -= (_impl.get_weight(b_iter->second) - b_iter->first);
0603             if(a_iter->first < normalized_average) {
0604                 *b_iter = *a_iter++;
0605             } else {
0606                 ++b_iter;
0607             }
0608         }
0609         for(; b_iter != b_end; ++b_iter) {
0610             _impl._alias_table[static_cast<std::size_t>(b_iter->second)].first =
0611                 _impl.get_weight(b_iter->second);
0612         }
0613         for(; a_iter != a_end; ++a_iter) {
0614             _impl._alias_table[static_cast<std::size_t>(a_iter->second)].first =
0615                 _impl.get_weight(a_iter->second);
0616         }
0617     }
0618     template<class Iter>
0619     void init(Iter first, Iter last)
0620     {
0621         if(first == last) {
0622             _impl.init_empty();
0623         } else {
0624             typename std::iterator_traits<Iter>::iterator_category category;
0625             init(first, last, category);
0626         }
0627     }
0628     typedef typename detail::select_alias_table<
0629         (::boost::is_integral<WeightType>::value)
0630     >::template apply<IntType, WeightType>::type impl_type;
0631     impl_type _impl;
0632     /// @endcond
0633 };
0634 
0635 }
0636 }
0637 
0638 #include <boost/random/detail/enable_warnings.hpp>
0639 
0640 #endif