File indexing completed on 2025-01-30 09:59:11
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
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
0054
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 )
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>& )
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
0222
0223
0224
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
0239
0240
0241 param_type() : _probabilities(1, static_cast<WeightType>(1)) {}
0242
0243
0244
0245
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
0255
0256
0257
0258 param_type(const std::initializer_list<WeightType>& wl)
0259 : _probabilities(wl)
0260 {
0261 normalize();
0262 }
0263 #endif
0264
0265
0266
0267
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
0278
0279
0280
0281
0282
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
0298
0299
0300 std::vector<WeightType> probabilities() const
0301 {
0302 return _probabilities;
0303 }
0304
0305
0306 BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
0307 {
0308 detail::print_vector(os, parm._probabilities);
0309 return os;
0310 }
0311
0312
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
0324 BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
0325 {
0326 return lhs._probabilities == rhs._probabilities;
0327 }
0328
0329 BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
0330 private:
0331
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
0342 };
0343
0344
0345
0346
0347
0348 discrete_distribution()
0349 {
0350 _impl.init_empty();
0351 }
0352
0353
0354
0355
0356
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
0366
0367
0368
0369
0370
0371
0372
0373
0374
0375
0376
0377
0378 discrete_distribution(std::initializer_list<WeightType> wl)
0379 {
0380 init(wl.begin(), wl.end());
0381 }
0382 #endif
0383
0384
0385
0386
0387
0388
0389 template<class Range>
0390 explicit discrete_distribution(const Range& range)
0391 {
0392 init(boost::begin(range), boost::end(range));
0393 }
0394
0395
0396
0397
0398
0399
0400
0401
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
0417
0418 explicit discrete_distribution(const param_type& parm)
0419 {
0420 param(parm);
0421 }
0422
0423
0424
0425
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
0446
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
0466
0467
0468 return static_cast<IntType>(parm._probabilities.size() - 1);
0469 } else {
0470
0471
0472 return discrete_distribution(parm)(urng);
0473 }
0474 }
0475
0476
0477 result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 0; }
0478
0479 result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
0480 { return static_cast<result_type>(_impl._alias_table.size() - 1); }
0481
0482
0483
0484
0485
0486
0487
0488
0489
0490
0491
0492
0493
0494
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
0514 param_type param() const
0515 {
0516 return param_type(*this);
0517 }
0518
0519 void param(const param_type& parm)
0520 {
0521 init(parm._probabilities.begin(), parm._probabilities.end());
0522 }
0523
0524
0525
0526
0527
0528 void reset() {}
0529
0530
0531 BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, discrete_distribution, dd)
0532 {
0533 os << dd.param();
0534 return os;
0535 }
0536
0537
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
0549
0550
0551 BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(discrete_distribution, lhs, rhs)
0552 {
0553 return lhs._impl == rhs._impl;
0554 }
0555
0556
0557
0558
0559 BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(discrete_distribution)
0560
0561 private:
0562
0563
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
0633 };
0634
0635 }
0636 }
0637
0638 #include <boost/random/detail/enable_warnings.hpp>
0639
0640 #endif