annotate DEPENDENCIES/generic/include/boost/random/discrete_distribution.hpp @ 16:2665513ce2d3

Add boost headers
author Chris Cannam
date Tue, 05 Aug 2014 11:11:38 +0100
parents
children c530137014c0
rev   line source
Chris@16 1 /* boost random/discrete_distribution.hpp header file
Chris@16 2 *
Chris@16 3 * Copyright Steven Watanabe 2009-2011
Chris@16 4 * Distributed under the Boost Software License, Version 1.0. (See
Chris@16 5 * accompanying file LICENSE_1_0.txt or copy at
Chris@16 6 * http://www.boost.org/LICENSE_1_0.txt)
Chris@16 7 *
Chris@16 8 * See http://www.boost.org for most recent version including documentation.
Chris@16 9 *
Chris@16 10 * $Id: discrete_distribution.hpp 85813 2013-09-21 20:17:00Z jewillco $
Chris@16 11 */
Chris@16 12
Chris@16 13 #ifndef BOOST_RANDOM_DISCRETE_DISTRIBUTION_HPP_INCLUDED
Chris@16 14 #define BOOST_RANDOM_DISCRETE_DISTRIBUTION_HPP_INCLUDED
Chris@16 15
Chris@16 16 #include <vector>
Chris@16 17 #include <limits>
Chris@16 18 #include <numeric>
Chris@16 19 #include <utility>
Chris@16 20 #include <iterator>
Chris@16 21 #include <boost/assert.hpp>
Chris@16 22 #include <boost/random/uniform_01.hpp>
Chris@16 23 #include <boost/random/uniform_int.hpp>
Chris@16 24 #include <boost/random/detail/config.hpp>
Chris@16 25 #include <boost/random/detail/operators.hpp>
Chris@16 26 #include <boost/random/detail/vector_io.hpp>
Chris@16 27
Chris@16 28 #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
Chris@16 29 #include <initializer_list>
Chris@16 30 #endif
Chris@16 31
Chris@16 32 #include <boost/range/begin.hpp>
Chris@16 33 #include <boost/range/end.hpp>
Chris@16 34
Chris@16 35 #include <boost/random/detail/disable_warnings.hpp>
Chris@16 36
Chris@16 37 namespace boost {
Chris@16 38 namespace random {
Chris@16 39
Chris@16 40 /**
Chris@16 41 * The class @c discrete_distribution models a \random_distribution.
Chris@16 42 * It produces integers in the range [0, n) with the probability
Chris@16 43 * of producing each value is specified by the parameters of the
Chris@16 44 * distribution.
Chris@16 45 */
Chris@16 46 template<class IntType = int, class WeightType = double>
Chris@16 47 class discrete_distribution {
Chris@16 48 public:
Chris@16 49 typedef WeightType input_type;
Chris@16 50 typedef IntType result_type;
Chris@16 51
Chris@16 52 class param_type {
Chris@16 53 public:
Chris@16 54
Chris@16 55 typedef discrete_distribution distribution_type;
Chris@16 56
Chris@16 57 /**
Chris@16 58 * Constructs a @c param_type object, representing a distribution
Chris@16 59 * with \f$p(0) = 1\f$ and \f$p(k|k>0) = 0\f$.
Chris@16 60 */
Chris@16 61 param_type() : _probabilities(1, static_cast<WeightType>(1)) {}
Chris@16 62 /**
Chris@16 63 * If @c first == @c last, equivalent to the default constructor.
Chris@16 64 * Otherwise, the values of the range represent weights for the
Chris@16 65 * possible values of the distribution.
Chris@16 66 */
Chris@16 67 template<class Iter>
Chris@16 68 param_type(Iter first, Iter last) : _probabilities(first, last)
Chris@16 69 {
Chris@16 70 normalize();
Chris@16 71 }
Chris@16 72 #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
Chris@16 73 /**
Chris@16 74 * If wl.size() == 0, equivalent to the default constructor.
Chris@16 75 * Otherwise, the values of the @c initializer_list represent
Chris@16 76 * weights for the possible values of the distribution.
Chris@16 77 */
Chris@16 78 param_type(const std::initializer_list<WeightType>& wl)
Chris@16 79 : _probabilities(wl)
Chris@16 80 {
Chris@16 81 normalize();
Chris@16 82 }
Chris@16 83 #endif
Chris@16 84 /**
Chris@16 85 * If the range is empty, equivalent to the default constructor.
Chris@16 86 * Otherwise, the elements of the range represent
Chris@16 87 * weights for the possible values of the distribution.
Chris@16 88 */
Chris@16 89 template<class Range>
Chris@16 90 explicit param_type(const Range& range)
Chris@16 91 : _probabilities(boost::begin(range), boost::end(range))
Chris@16 92 {
Chris@16 93 normalize();
Chris@16 94 }
Chris@16 95
Chris@16 96 /**
Chris@16 97 * If nw is zero, equivalent to the default constructor.
Chris@16 98 * Otherwise, the range of the distribution is [0, nw),
Chris@16 99 * and the weights are found by calling fw with values
Chris@16 100 * evenly distributed between \f$\mbox{xmin} + \delta/2\f$ and
Chris@16 101 * \f$\mbox{xmax} - \delta/2\f$, where
Chris@16 102 * \f$\delta = (\mbox{xmax} - \mbox{xmin})/\mbox{nw}\f$.
Chris@16 103 */
Chris@16 104 template<class Func>
Chris@16 105 param_type(std::size_t nw, double xmin, double xmax, Func fw)
Chris@16 106 {
Chris@16 107 std::size_t n = (nw == 0) ? 1 : nw;
Chris@16 108 double delta = (xmax - xmin) / n;
Chris@16 109 BOOST_ASSERT(delta > 0);
Chris@16 110 for(std::size_t k = 0; k < n; ++k) {
Chris@16 111 _probabilities.push_back(fw(xmin + k*delta + delta/2));
Chris@16 112 }
Chris@16 113 normalize();
Chris@16 114 }
Chris@16 115
Chris@16 116 /**
Chris@16 117 * Returns a vector containing the probabilities of each possible
Chris@16 118 * value of the distribution.
Chris@16 119 */
Chris@16 120 std::vector<WeightType> probabilities() const
Chris@16 121 {
Chris@16 122 return _probabilities;
Chris@16 123 }
Chris@16 124
Chris@16 125 /** Writes the parameters to a @c std::ostream. */
Chris@16 126 BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
Chris@16 127 {
Chris@16 128 detail::print_vector(os, parm._probabilities);
Chris@16 129 return os;
Chris@16 130 }
Chris@16 131
Chris@16 132 /** Reads the parameters from a @c std::istream. */
Chris@16 133 BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
Chris@16 134 {
Chris@16 135 std::vector<WeightType> temp;
Chris@16 136 detail::read_vector(is, temp);
Chris@16 137 if(is) {
Chris@16 138 parm._probabilities.swap(temp);
Chris@16 139 }
Chris@16 140 return is;
Chris@16 141 }
Chris@16 142
Chris@16 143 /** Returns true if the two sets of parameters are the same. */
Chris@16 144 BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
Chris@16 145 {
Chris@16 146 return lhs._probabilities == rhs._probabilities;
Chris@16 147 }
Chris@16 148 /** Returns true if the two sets of parameters are different. */
Chris@16 149 BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
Chris@16 150 private:
Chris@16 151 /// @cond show_private
Chris@16 152 friend class discrete_distribution;
Chris@16 153 explicit param_type(const discrete_distribution& dist)
Chris@16 154 : _probabilities(dist.probabilities())
Chris@16 155 {}
Chris@16 156 void normalize()
Chris@16 157 {
Chris@16 158 WeightType sum =
Chris@16 159 std::accumulate(_probabilities.begin(), _probabilities.end(),
Chris@16 160 static_cast<WeightType>(0));
Chris@16 161 for(typename std::vector<WeightType>::iterator
Chris@16 162 iter = _probabilities.begin(),
Chris@16 163 end = _probabilities.end();
Chris@16 164 iter != end; ++iter)
Chris@16 165 {
Chris@16 166 *iter /= sum;
Chris@16 167 }
Chris@16 168 }
Chris@16 169 std::vector<WeightType> _probabilities;
Chris@16 170 /// @endcond
Chris@16 171 };
Chris@16 172
Chris@16 173 /**
Chris@16 174 * Creates a new @c discrete_distribution object that has
Chris@16 175 * \f$p(0) = 1\f$ and \f$p(i|i>0) = 0\f$.
Chris@16 176 */
Chris@16 177 discrete_distribution()
Chris@16 178 {
Chris@16 179 _alias_table.push_back(std::make_pair(static_cast<WeightType>(1),
Chris@16 180 static_cast<IntType>(0)));
Chris@16 181 }
Chris@16 182 /**
Chris@16 183 * Constructs a discrete_distribution from an iterator range.
Chris@16 184 * If @c first == @c last, equivalent to the default constructor.
Chris@16 185 * Otherwise, the values of the range represent weights for the
Chris@16 186 * possible values of the distribution.
Chris@16 187 */
Chris@16 188 template<class Iter>
Chris@16 189 discrete_distribution(Iter first, Iter last)
Chris@16 190 {
Chris@16 191 init(first, last);
Chris@16 192 }
Chris@16 193 #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
Chris@16 194 /**
Chris@16 195 * Constructs a @c discrete_distribution from a @c std::initializer_list.
Chris@16 196 * If the @c initializer_list is empty, equivalent to the default
Chris@16 197 * constructor. Otherwise, the values of the @c initializer_list
Chris@16 198 * represent weights for the possible values of the distribution.
Chris@16 199 * For example, given the distribution
Chris@16 200 *
Chris@16 201 * @code
Chris@16 202 * discrete_distribution<> dist{1, 4, 5};
Chris@16 203 * @endcode
Chris@16 204 *
Chris@16 205 * The probability of a 0 is 1/10, the probability of a 1 is 2/5,
Chris@16 206 * the probability of a 2 is 1/2, and no other values are possible.
Chris@16 207 */
Chris@16 208 discrete_distribution(std::initializer_list<WeightType> wl)
Chris@16 209 {
Chris@16 210 init(wl.begin(), wl.end());
Chris@16 211 }
Chris@16 212 #endif
Chris@16 213 /**
Chris@16 214 * Constructs a discrete_distribution from a Boost.Range range.
Chris@16 215 * If the range is empty, equivalent to the default constructor.
Chris@16 216 * Otherwise, the values of the range represent weights for the
Chris@16 217 * possible values of the distribution.
Chris@16 218 */
Chris@16 219 template<class Range>
Chris@16 220 explicit discrete_distribution(const Range& range)
Chris@16 221 {
Chris@16 222 init(boost::begin(range), boost::end(range));
Chris@16 223 }
Chris@16 224 /**
Chris@16 225 * Constructs a discrete_distribution that approximates a function.
Chris@16 226 * If nw is zero, equivalent to the default constructor.
Chris@16 227 * Otherwise, the range of the distribution is [0, nw),
Chris@16 228 * and the weights are found by calling fw with values
Chris@16 229 * evenly distributed between \f$\mbox{xmin} + \delta/2\f$ and
Chris@16 230 * \f$\mbox{xmax} - \delta/2\f$, where
Chris@16 231 * \f$\delta = (\mbox{xmax} - \mbox{xmin})/\mbox{nw}\f$.
Chris@16 232 */
Chris@16 233 template<class Func>
Chris@16 234 discrete_distribution(std::size_t nw, double xmin, double xmax, Func fw)
Chris@16 235 {
Chris@16 236 std::size_t n = (nw == 0) ? 1 : nw;
Chris@16 237 double delta = (xmax - xmin) / n;
Chris@16 238 BOOST_ASSERT(delta > 0);
Chris@16 239 std::vector<WeightType> weights;
Chris@16 240 for(std::size_t k = 0; k < n; ++k) {
Chris@16 241 weights.push_back(fw(xmin + k*delta + delta/2));
Chris@16 242 }
Chris@16 243 init(weights.begin(), weights.end());
Chris@16 244 }
Chris@16 245 /**
Chris@16 246 * Constructs a discrete_distribution from its parameters.
Chris@16 247 */
Chris@16 248 explicit discrete_distribution(const param_type& parm)
Chris@16 249 {
Chris@16 250 param(parm);
Chris@16 251 }
Chris@16 252
Chris@16 253 /**
Chris@16 254 * Returns a value distributed according to the parameters of the
Chris@16 255 * discrete_distribution.
Chris@16 256 */
Chris@16 257 template<class URNG>
Chris@16 258 IntType operator()(URNG& urng) const
Chris@16 259 {
Chris@16 260 BOOST_ASSERT(!_alias_table.empty());
Chris@16 261 WeightType test = uniform_01<WeightType>()(urng);
Chris@16 262 IntType result = uniform_int<IntType>((min)(), (max)())(urng);
Chris@16 263 if(test < _alias_table[result].first) {
Chris@16 264 return result;
Chris@16 265 } else {
Chris@16 266 return(_alias_table[result].second);
Chris@16 267 }
Chris@16 268 }
Chris@16 269
Chris@16 270 /**
Chris@16 271 * Returns a value distributed according to the parameters
Chris@16 272 * specified by param.
Chris@16 273 */
Chris@16 274 template<class URNG>
Chris@16 275 IntType operator()(URNG& urng, const param_type& parm) const
Chris@16 276 {
Chris@16 277 while(true) {
Chris@16 278 WeightType val = uniform_01<WeightType>()(urng);
Chris@16 279 WeightType sum = 0;
Chris@16 280 std::size_t result = 0;
Chris@16 281 for(typename std::vector<WeightType>::const_iterator
Chris@16 282 iter = parm._probabilities.begin(),
Chris@16 283 end = parm._probabilities.end();
Chris@16 284 iter != end; ++iter, ++result)
Chris@16 285 {
Chris@16 286 sum += *iter;
Chris@16 287 if(sum > val) {
Chris@16 288 return result;
Chris@16 289 }
Chris@16 290 }
Chris@16 291 }
Chris@16 292 }
Chris@16 293
Chris@16 294 /** Returns the smallest value that the distribution can produce. */
Chris@16 295 result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 0; }
Chris@16 296 /** Returns the largest value that the distribution can produce. */
Chris@16 297 result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
Chris@16 298 { return static_cast<result_type>(_alias_table.size() - 1); }
Chris@16 299
Chris@16 300 /**
Chris@16 301 * Returns a vector containing the probabilities of each
Chris@16 302 * value of the distribution. For example, given
Chris@16 303 *
Chris@16 304 * @code
Chris@16 305 * discrete_distribution<> dist = { 1, 4, 5 };
Chris@16 306 * std::vector<double> p = dist.param();
Chris@16 307 * @endcode
Chris@16 308 *
Chris@16 309 * the vector, p will contain {0.1, 0.4, 0.5}.
Chris@16 310 */
Chris@16 311 std::vector<WeightType> probabilities() const
Chris@16 312 {
Chris@16 313 std::vector<WeightType> result(_alias_table.size());
Chris@16 314 const WeightType mean =
Chris@16 315 static_cast<WeightType>(1) / _alias_table.size();
Chris@16 316 std::size_t i = 0;
Chris@16 317 for(typename alias_table_t::const_iterator
Chris@16 318 iter = _alias_table.begin(),
Chris@16 319 end = _alias_table.end();
Chris@16 320 iter != end; ++iter, ++i)
Chris@16 321 {
Chris@16 322 WeightType val = iter->first * mean;
Chris@16 323 result[i] += val;
Chris@16 324 result[iter->second] += mean - val;
Chris@16 325 }
Chris@16 326 return(result);
Chris@16 327 }
Chris@16 328
Chris@16 329 /** Returns the parameters of the distribution. */
Chris@16 330 param_type param() const
Chris@16 331 {
Chris@16 332 return param_type(*this);
Chris@16 333 }
Chris@16 334 /** Sets the parameters of the distribution. */
Chris@16 335 void param(const param_type& parm)
Chris@16 336 {
Chris@16 337 init(parm._probabilities.begin(), parm._probabilities.end());
Chris@16 338 }
Chris@16 339
Chris@16 340 /**
Chris@16 341 * Effects: Subsequent uses of the distribution do not depend
Chris@16 342 * on values produced by any engine prior to invoking reset.
Chris@16 343 */
Chris@16 344 void reset() {}
Chris@16 345
Chris@16 346 /** Writes a distribution to a @c std::ostream. */
Chris@16 347 BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, discrete_distribution, dd)
Chris@16 348 {
Chris@16 349 os << dd.param();
Chris@16 350 return os;
Chris@16 351 }
Chris@16 352
Chris@16 353 /** Reads a distribution from a @c std::istream */
Chris@16 354 BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, discrete_distribution, dd)
Chris@16 355 {
Chris@16 356 param_type parm;
Chris@16 357 if(is >> parm) {
Chris@16 358 dd.param(parm);
Chris@16 359 }
Chris@16 360 return is;
Chris@16 361 }
Chris@16 362
Chris@16 363 /**
Chris@16 364 * Returns true if the two distributions will return the
Chris@16 365 * same sequence of values, when passed equal generators.
Chris@16 366 */
Chris@16 367 BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(discrete_distribution, lhs, rhs)
Chris@16 368 {
Chris@16 369 return lhs._alias_table == rhs._alias_table;
Chris@16 370 }
Chris@16 371 /**
Chris@16 372 * Returns true if the two distributions may return different
Chris@16 373 * sequences of values, when passed equal generators.
Chris@16 374 */
Chris@16 375 BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(discrete_distribution)
Chris@16 376
Chris@16 377 private:
Chris@16 378
Chris@16 379 /// @cond show_private
Chris@16 380
Chris@16 381 template<class Iter>
Chris@16 382 void init(Iter first, Iter last, std::input_iterator_tag)
Chris@16 383 {
Chris@16 384 std::vector<WeightType> temp(first, last);
Chris@16 385 init(temp.begin(), temp.end());
Chris@16 386 }
Chris@16 387 template<class Iter>
Chris@16 388 void init(Iter first, Iter last, std::forward_iterator_tag)
Chris@16 389 {
Chris@16 390 std::vector<std::pair<WeightType, IntType> > below_average;
Chris@16 391 std::vector<std::pair<WeightType, IntType> > above_average;
Chris@16 392 std::size_t size = std::distance(first, last);
Chris@16 393 WeightType weight_sum =
Chris@16 394 std::accumulate(first, last, static_cast<WeightType>(0));
Chris@16 395 WeightType weight_average = weight_sum / size;
Chris@16 396 std::size_t i = 0;
Chris@16 397 for(; first != last; ++first, ++i) {
Chris@16 398 WeightType val = *first / weight_average;
Chris@16 399 std::pair<WeightType, IntType> elem(val, static_cast<IntType>(i));
Chris@16 400 if(val < static_cast<WeightType>(1)) {
Chris@16 401 below_average.push_back(elem);
Chris@16 402 } else {
Chris@16 403 above_average.push_back(elem);
Chris@16 404 }
Chris@16 405 }
Chris@16 406
Chris@16 407 _alias_table.resize(size);
Chris@16 408 typename alias_table_t::iterator
Chris@16 409 b_iter = below_average.begin(),
Chris@16 410 b_end = below_average.end(),
Chris@16 411 a_iter = above_average.begin(),
Chris@16 412 a_end = above_average.end()
Chris@16 413 ;
Chris@16 414 while(b_iter != b_end && a_iter != a_end) {
Chris@16 415 _alias_table[b_iter->second] =
Chris@16 416 std::make_pair(b_iter->first, a_iter->second);
Chris@16 417 a_iter->first -= (static_cast<WeightType>(1) - b_iter->first);
Chris@16 418 if(a_iter->first < static_cast<WeightType>(1)) {
Chris@16 419 *b_iter = *a_iter++;
Chris@16 420 } else {
Chris@16 421 ++b_iter;
Chris@16 422 }
Chris@16 423 }
Chris@16 424 for(; b_iter != b_end; ++b_iter) {
Chris@16 425 _alias_table[b_iter->second].first = static_cast<WeightType>(1);
Chris@16 426 }
Chris@16 427 for(; a_iter != a_end; ++a_iter) {
Chris@16 428 _alias_table[a_iter->second].first = static_cast<WeightType>(1);
Chris@16 429 }
Chris@16 430 }
Chris@16 431 template<class Iter>
Chris@16 432 void init(Iter first, Iter last)
Chris@16 433 {
Chris@16 434 if(first == last) {
Chris@16 435 _alias_table.clear();
Chris@16 436 _alias_table.push_back(std::make_pair(static_cast<WeightType>(1),
Chris@16 437 static_cast<IntType>(0)));
Chris@16 438 } else {
Chris@16 439 typename std::iterator_traits<Iter>::iterator_category category;
Chris@16 440 init(first, last, category);
Chris@16 441 }
Chris@16 442 }
Chris@16 443 typedef std::vector<std::pair<WeightType, IntType> > alias_table_t;
Chris@16 444 alias_table_t _alias_table;
Chris@16 445 /// @endcond
Chris@16 446 };
Chris@16 447
Chris@16 448 }
Chris@16 449 }
Chris@16 450
Chris@16 451 #include <boost/random/detail/enable_warnings.hpp>
Chris@16 452
Chris@16 453 #endif