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@101
|
10 * $Id$
|
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@101
|
23 #include <boost/random/uniform_int_distribution.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@101
|
39 namespace detail {
|
Chris@101
|
40
|
Chris@101
|
41 template<class IntType, class WeightType>
|
Chris@101
|
42 struct integer_alias_table {
|
Chris@101
|
43 WeightType get_weight(IntType bin) const {
|
Chris@101
|
44 WeightType result = _average;
|
Chris@101
|
45 if(bin < _excess) ++result;
|
Chris@101
|
46 return result;
|
Chris@101
|
47 }
|
Chris@101
|
48 template<class Iter>
|
Chris@101
|
49 WeightType init_average(Iter begin, Iter end) {
|
Chris@101
|
50 WeightType weight_average = 0;
|
Chris@101
|
51 IntType excess = 0;
|
Chris@101
|
52 IntType n = 0;
|
Chris@101
|
53 // weight_average * n + excess == current partial sum
|
Chris@101
|
54 // This is a bit messy, but it's guaranteed not to overflow
|
Chris@101
|
55 for(Iter iter = begin; iter != end; ++iter) {
|
Chris@101
|
56 ++n;
|
Chris@101
|
57 if(*iter < weight_average) {
|
Chris@101
|
58 WeightType diff = weight_average - *iter;
|
Chris@101
|
59 weight_average -= diff / n;
|
Chris@101
|
60 if(diff % n > excess) {
|
Chris@101
|
61 --weight_average;
|
Chris@101
|
62 excess += n - diff % n;
|
Chris@101
|
63 } else {
|
Chris@101
|
64 excess -= diff % n;
|
Chris@101
|
65 }
|
Chris@101
|
66 } else {
|
Chris@101
|
67 WeightType diff = *iter - weight_average;
|
Chris@101
|
68 weight_average += diff / n;
|
Chris@101
|
69 if(diff % n < n - excess) {
|
Chris@101
|
70 excess += diff % n;
|
Chris@101
|
71 } else {
|
Chris@101
|
72 ++weight_average;
|
Chris@101
|
73 excess -= n - diff % n;
|
Chris@101
|
74 }
|
Chris@101
|
75 }
|
Chris@101
|
76 }
|
Chris@101
|
77 _alias_table.resize(static_cast<std::size_t>(n));
|
Chris@101
|
78 _average = weight_average;
|
Chris@101
|
79 _excess = excess;
|
Chris@101
|
80 return weight_average;
|
Chris@101
|
81 }
|
Chris@101
|
82 void init_empty()
|
Chris@101
|
83 {
|
Chris@101
|
84 _alias_table.clear();
|
Chris@101
|
85 _alias_table.push_back(std::make_pair(static_cast<WeightType>(1),
|
Chris@101
|
86 static_cast<IntType>(0)));
|
Chris@101
|
87 _average = static_cast<WeightType>(1);
|
Chris@101
|
88 _excess = static_cast<IntType>(0);
|
Chris@101
|
89 }
|
Chris@101
|
90 bool operator==(const integer_alias_table& other) const
|
Chris@101
|
91 {
|
Chris@101
|
92 return _alias_table == other._alias_table &&
|
Chris@101
|
93 _average == other._average && _excess == other._excess;
|
Chris@101
|
94 }
|
Chris@101
|
95 static WeightType normalize(WeightType val, WeightType average)
|
Chris@101
|
96 {
|
Chris@101
|
97 return val;
|
Chris@101
|
98 }
|
Chris@101
|
99 static void normalize(std::vector<WeightType>&) {}
|
Chris@101
|
100 template<class URNG>
|
Chris@101
|
101 WeightType test(URNG &urng) const
|
Chris@101
|
102 {
|
Chris@101
|
103 return uniform_int_distribution<WeightType>(0, _average)(urng);
|
Chris@101
|
104 }
|
Chris@101
|
105 bool accept(IntType result, WeightType val) const
|
Chris@101
|
106 {
|
Chris@101
|
107 return result < _excess || val < _average;
|
Chris@101
|
108 }
|
Chris@101
|
109 static WeightType try_get_sum(const std::vector<WeightType>& weights)
|
Chris@101
|
110 {
|
Chris@101
|
111 WeightType result = static_cast<WeightType>(0);
|
Chris@101
|
112 for(typename std::vector<WeightType>::const_iterator
|
Chris@101
|
113 iter = weights.begin(), end = weights.end();
|
Chris@101
|
114 iter != end; ++iter)
|
Chris@101
|
115 {
|
Chris@101
|
116 if((std::numeric_limits<WeightType>::max)() - result > *iter) {
|
Chris@101
|
117 return static_cast<WeightType>(0);
|
Chris@101
|
118 }
|
Chris@101
|
119 result += *iter;
|
Chris@101
|
120 }
|
Chris@101
|
121 return result;
|
Chris@101
|
122 }
|
Chris@101
|
123 template<class URNG>
|
Chris@101
|
124 static WeightType generate_in_range(URNG &urng, WeightType max)
|
Chris@101
|
125 {
|
Chris@101
|
126 return uniform_int_distribution<WeightType>(
|
Chris@101
|
127 static_cast<WeightType>(0), max-1)(urng);
|
Chris@101
|
128 }
|
Chris@101
|
129 typedef std::vector<std::pair<WeightType, IntType> > alias_table_t;
|
Chris@101
|
130 alias_table_t _alias_table;
|
Chris@101
|
131 WeightType _average;
|
Chris@101
|
132 IntType _excess;
|
Chris@101
|
133 };
|
Chris@101
|
134
|
Chris@101
|
135 template<class IntType, class WeightType>
|
Chris@101
|
136 struct real_alias_table {
|
Chris@101
|
137 WeightType get_weight(IntType) const
|
Chris@101
|
138 {
|
Chris@101
|
139 return WeightType(1.0);
|
Chris@101
|
140 }
|
Chris@101
|
141 template<class Iter>
|
Chris@101
|
142 WeightType init_average(Iter first, Iter last)
|
Chris@101
|
143 {
|
Chris@101
|
144 std::size_t size = std::distance(first, last);
|
Chris@101
|
145 WeightType weight_sum =
|
Chris@101
|
146 std::accumulate(first, last, static_cast<WeightType>(0));
|
Chris@101
|
147 _alias_table.resize(size);
|
Chris@101
|
148 return weight_sum / size;
|
Chris@101
|
149 }
|
Chris@101
|
150 void init_empty()
|
Chris@101
|
151 {
|
Chris@101
|
152 _alias_table.clear();
|
Chris@101
|
153 _alias_table.push_back(std::make_pair(static_cast<WeightType>(1),
|
Chris@101
|
154 static_cast<IntType>(0)));
|
Chris@101
|
155 }
|
Chris@101
|
156 bool operator==(const real_alias_table& other) const
|
Chris@101
|
157 {
|
Chris@101
|
158 return _alias_table == other._alias_table;
|
Chris@101
|
159 }
|
Chris@101
|
160 static WeightType normalize(WeightType val, WeightType average)
|
Chris@101
|
161 {
|
Chris@101
|
162 return val / average;
|
Chris@101
|
163 }
|
Chris@101
|
164 static void normalize(std::vector<WeightType>& weights)
|
Chris@101
|
165 {
|
Chris@101
|
166 WeightType sum =
|
Chris@101
|
167 std::accumulate(weights.begin(), weights.end(),
|
Chris@101
|
168 static_cast<WeightType>(0));
|
Chris@101
|
169 for(typename std::vector<WeightType>::iterator
|
Chris@101
|
170 iter = weights.begin(),
|
Chris@101
|
171 end = weights.end();
|
Chris@101
|
172 iter != end; ++iter)
|
Chris@101
|
173 {
|
Chris@101
|
174 *iter /= sum;
|
Chris@101
|
175 }
|
Chris@101
|
176 }
|
Chris@101
|
177 template<class URNG>
|
Chris@101
|
178 WeightType test(URNG &urng) const
|
Chris@101
|
179 {
|
Chris@101
|
180 return uniform_01<WeightType>()(urng);
|
Chris@101
|
181 }
|
Chris@101
|
182 bool accept(IntType, WeightType) const
|
Chris@101
|
183 {
|
Chris@101
|
184 return true;
|
Chris@101
|
185 }
|
Chris@101
|
186 static WeightType try_get_sum(const std::vector<WeightType>& weights)
|
Chris@101
|
187 {
|
Chris@101
|
188 return static_cast<WeightType>(1);
|
Chris@101
|
189 }
|
Chris@101
|
190 template<class URNG>
|
Chris@101
|
191 static WeightType generate_in_range(URNG &urng, WeightType)
|
Chris@101
|
192 {
|
Chris@101
|
193 return uniform_01<WeightType>()(urng);
|
Chris@101
|
194 }
|
Chris@101
|
195 typedef std::vector<std::pair<WeightType, IntType> > alias_table_t;
|
Chris@101
|
196 alias_table_t _alias_table;
|
Chris@101
|
197 };
|
Chris@101
|
198
|
Chris@101
|
199 template<bool IsIntegral>
|
Chris@101
|
200 struct select_alias_table;
|
Chris@101
|
201
|
Chris@101
|
202 template<>
|
Chris@101
|
203 struct select_alias_table<true> {
|
Chris@101
|
204 template<class IntType, class WeightType>
|
Chris@101
|
205 struct apply {
|
Chris@101
|
206 typedef integer_alias_table<IntType, WeightType> type;
|
Chris@101
|
207 };
|
Chris@101
|
208 };
|
Chris@101
|
209
|
Chris@101
|
210 template<>
|
Chris@101
|
211 struct select_alias_table<false> {
|
Chris@101
|
212 template<class IntType, class WeightType>
|
Chris@101
|
213 struct apply {
|
Chris@101
|
214 typedef real_alias_table<IntType, WeightType> type;
|
Chris@101
|
215 };
|
Chris@101
|
216 };
|
Chris@101
|
217
|
Chris@101
|
218 }
|
Chris@16
|
219
|
Chris@16
|
220 /**
|
Chris@16
|
221 * The class @c discrete_distribution models a \random_distribution.
|
Chris@16
|
222 * It produces integers in the range [0, n) with the probability
|
Chris@16
|
223 * of producing each value is specified by the parameters of the
|
Chris@16
|
224 * distribution.
|
Chris@16
|
225 */
|
Chris@16
|
226 template<class IntType = int, class WeightType = double>
|
Chris@16
|
227 class discrete_distribution {
|
Chris@16
|
228 public:
|
Chris@16
|
229 typedef WeightType input_type;
|
Chris@16
|
230 typedef IntType result_type;
|
Chris@16
|
231
|
Chris@16
|
232 class param_type {
|
Chris@16
|
233 public:
|
Chris@16
|
234
|
Chris@16
|
235 typedef discrete_distribution distribution_type;
|
Chris@16
|
236
|
Chris@16
|
237 /**
|
Chris@16
|
238 * Constructs a @c param_type object, representing a distribution
|
Chris@16
|
239 * with \f$p(0) = 1\f$ and \f$p(k|k>0) = 0\f$.
|
Chris@16
|
240 */
|
Chris@16
|
241 param_type() : _probabilities(1, static_cast<WeightType>(1)) {}
|
Chris@16
|
242 /**
|
Chris@16
|
243 * If @c first == @c last, equivalent to the default constructor.
|
Chris@16
|
244 * Otherwise, the values of the range represent weights for the
|
Chris@16
|
245 * possible values of the distribution.
|
Chris@16
|
246 */
|
Chris@16
|
247 template<class Iter>
|
Chris@16
|
248 param_type(Iter first, Iter last) : _probabilities(first, last)
|
Chris@16
|
249 {
|
Chris@16
|
250 normalize();
|
Chris@16
|
251 }
|
Chris@16
|
252 #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
|
Chris@16
|
253 /**
|
Chris@16
|
254 * If wl.size() == 0, equivalent to the default constructor.
|
Chris@16
|
255 * Otherwise, the values of the @c initializer_list represent
|
Chris@16
|
256 * weights for the possible values of the distribution.
|
Chris@16
|
257 */
|
Chris@16
|
258 param_type(const std::initializer_list<WeightType>& wl)
|
Chris@16
|
259 : _probabilities(wl)
|
Chris@16
|
260 {
|
Chris@16
|
261 normalize();
|
Chris@16
|
262 }
|
Chris@16
|
263 #endif
|
Chris@16
|
264 /**
|
Chris@16
|
265 * If the range is empty, equivalent to the default constructor.
|
Chris@16
|
266 * Otherwise, the elements of the range represent
|
Chris@16
|
267 * weights for the possible values of the distribution.
|
Chris@16
|
268 */
|
Chris@16
|
269 template<class Range>
|
Chris@16
|
270 explicit param_type(const Range& range)
|
Chris@16
|
271 : _probabilities(boost::begin(range), boost::end(range))
|
Chris@16
|
272 {
|
Chris@16
|
273 normalize();
|
Chris@16
|
274 }
|
Chris@16
|
275
|
Chris@16
|
276 /**
|
Chris@16
|
277 * If nw is zero, equivalent to the default constructor.
|
Chris@16
|
278 * Otherwise, the range of the distribution is [0, nw),
|
Chris@16
|
279 * and the weights are found by calling fw with values
|
Chris@16
|
280 * evenly distributed between \f$\mbox{xmin} + \delta/2\f$ and
|
Chris@16
|
281 * \f$\mbox{xmax} - \delta/2\f$, where
|
Chris@16
|
282 * \f$\delta = (\mbox{xmax} - \mbox{xmin})/\mbox{nw}\f$.
|
Chris@16
|
283 */
|
Chris@16
|
284 template<class Func>
|
Chris@16
|
285 param_type(std::size_t nw, double xmin, double xmax, Func fw)
|
Chris@16
|
286 {
|
Chris@16
|
287 std::size_t n = (nw == 0) ? 1 : nw;
|
Chris@16
|
288 double delta = (xmax - xmin) / n;
|
Chris@16
|
289 BOOST_ASSERT(delta > 0);
|
Chris@16
|
290 for(std::size_t k = 0; k < n; ++k) {
|
Chris@16
|
291 _probabilities.push_back(fw(xmin + k*delta + delta/2));
|
Chris@16
|
292 }
|
Chris@16
|
293 normalize();
|
Chris@16
|
294 }
|
Chris@16
|
295
|
Chris@16
|
296 /**
|
Chris@16
|
297 * Returns a vector containing the probabilities of each possible
|
Chris@16
|
298 * value of the distribution.
|
Chris@16
|
299 */
|
Chris@16
|
300 std::vector<WeightType> probabilities() const
|
Chris@16
|
301 {
|
Chris@16
|
302 return _probabilities;
|
Chris@16
|
303 }
|
Chris@16
|
304
|
Chris@16
|
305 /** Writes the parameters to a @c std::ostream. */
|
Chris@16
|
306 BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
|
Chris@16
|
307 {
|
Chris@16
|
308 detail::print_vector(os, parm._probabilities);
|
Chris@16
|
309 return os;
|
Chris@16
|
310 }
|
Chris@16
|
311
|
Chris@16
|
312 /** Reads the parameters from a @c std::istream. */
|
Chris@16
|
313 BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
|
Chris@16
|
314 {
|
Chris@16
|
315 std::vector<WeightType> temp;
|
Chris@16
|
316 detail::read_vector(is, temp);
|
Chris@16
|
317 if(is) {
|
Chris@16
|
318 parm._probabilities.swap(temp);
|
Chris@16
|
319 }
|
Chris@16
|
320 return is;
|
Chris@16
|
321 }
|
Chris@16
|
322
|
Chris@16
|
323 /** Returns true if the two sets of parameters are the same. */
|
Chris@16
|
324 BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
|
Chris@16
|
325 {
|
Chris@16
|
326 return lhs._probabilities == rhs._probabilities;
|
Chris@16
|
327 }
|
Chris@16
|
328 /** Returns true if the two sets of parameters are different. */
|
Chris@16
|
329 BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
|
Chris@16
|
330 private:
|
Chris@16
|
331 /// @cond show_private
|
Chris@16
|
332 friend class discrete_distribution;
|
Chris@16
|
333 explicit param_type(const discrete_distribution& dist)
|
Chris@16
|
334 : _probabilities(dist.probabilities())
|
Chris@16
|
335 {}
|
Chris@16
|
336 void normalize()
|
Chris@16
|
337 {
|
Chris@101
|
338 impl_type::normalize(_probabilities);
|
Chris@16
|
339 }
|
Chris@16
|
340 std::vector<WeightType> _probabilities;
|
Chris@16
|
341 /// @endcond
|
Chris@16
|
342 };
|
Chris@16
|
343
|
Chris@16
|
344 /**
|
Chris@16
|
345 * Creates a new @c discrete_distribution object that has
|
Chris@16
|
346 * \f$p(0) = 1\f$ and \f$p(i|i>0) = 0\f$.
|
Chris@16
|
347 */
|
Chris@16
|
348 discrete_distribution()
|
Chris@16
|
349 {
|
Chris@101
|
350 _impl.init_empty();
|
Chris@16
|
351 }
|
Chris@16
|
352 /**
|
Chris@16
|
353 * Constructs a discrete_distribution from an iterator range.
|
Chris@16
|
354 * If @c first == @c last, equivalent to the default constructor.
|
Chris@16
|
355 * Otherwise, the values of the range represent weights for the
|
Chris@16
|
356 * possible values of the distribution.
|
Chris@16
|
357 */
|
Chris@16
|
358 template<class Iter>
|
Chris@16
|
359 discrete_distribution(Iter first, Iter last)
|
Chris@16
|
360 {
|
Chris@16
|
361 init(first, last);
|
Chris@16
|
362 }
|
Chris@16
|
363 #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST
|
Chris@16
|
364 /**
|
Chris@16
|
365 * Constructs a @c discrete_distribution from a @c std::initializer_list.
|
Chris@16
|
366 * If the @c initializer_list is empty, equivalent to the default
|
Chris@16
|
367 * constructor. Otherwise, the values of the @c initializer_list
|
Chris@16
|
368 * represent weights for the possible values of the distribution.
|
Chris@16
|
369 * For example, given the distribution
|
Chris@16
|
370 *
|
Chris@16
|
371 * @code
|
Chris@16
|
372 * discrete_distribution<> dist{1, 4, 5};
|
Chris@16
|
373 * @endcode
|
Chris@16
|
374 *
|
Chris@16
|
375 * The probability of a 0 is 1/10, the probability of a 1 is 2/5,
|
Chris@16
|
376 * the probability of a 2 is 1/2, and no other values are possible.
|
Chris@16
|
377 */
|
Chris@16
|
378 discrete_distribution(std::initializer_list<WeightType> wl)
|
Chris@16
|
379 {
|
Chris@16
|
380 init(wl.begin(), wl.end());
|
Chris@16
|
381 }
|
Chris@16
|
382 #endif
|
Chris@16
|
383 /**
|
Chris@16
|
384 * Constructs a discrete_distribution from a Boost.Range range.
|
Chris@16
|
385 * If the range is empty, equivalent to the default constructor.
|
Chris@16
|
386 * Otherwise, the values of the range represent weights for the
|
Chris@16
|
387 * possible values of the distribution.
|
Chris@16
|
388 */
|
Chris@16
|
389 template<class Range>
|
Chris@16
|
390 explicit discrete_distribution(const Range& range)
|
Chris@16
|
391 {
|
Chris@16
|
392 init(boost::begin(range), boost::end(range));
|
Chris@16
|
393 }
|
Chris@16
|
394 /**
|
Chris@16
|
395 * Constructs a discrete_distribution that approximates a function.
|
Chris@16
|
396 * If nw is zero, equivalent to the default constructor.
|
Chris@16
|
397 * Otherwise, the range of the distribution is [0, nw),
|
Chris@16
|
398 * and the weights are found by calling fw with values
|
Chris@16
|
399 * evenly distributed between \f$\mbox{xmin} + \delta/2\f$ and
|
Chris@16
|
400 * \f$\mbox{xmax} - \delta/2\f$, where
|
Chris@16
|
401 * \f$\delta = (\mbox{xmax} - \mbox{xmin})/\mbox{nw}\f$.
|
Chris@16
|
402 */
|
Chris@16
|
403 template<class Func>
|
Chris@16
|
404 discrete_distribution(std::size_t nw, double xmin, double xmax, Func fw)
|
Chris@16
|
405 {
|
Chris@16
|
406 std::size_t n = (nw == 0) ? 1 : nw;
|
Chris@16
|
407 double delta = (xmax - xmin) / n;
|
Chris@16
|
408 BOOST_ASSERT(delta > 0);
|
Chris@16
|
409 std::vector<WeightType> weights;
|
Chris@16
|
410 for(std::size_t k = 0; k < n; ++k) {
|
Chris@16
|
411 weights.push_back(fw(xmin + k*delta + delta/2));
|
Chris@16
|
412 }
|
Chris@16
|
413 init(weights.begin(), weights.end());
|
Chris@16
|
414 }
|
Chris@16
|
415 /**
|
Chris@16
|
416 * Constructs a discrete_distribution from its parameters.
|
Chris@16
|
417 */
|
Chris@16
|
418 explicit discrete_distribution(const param_type& parm)
|
Chris@16
|
419 {
|
Chris@16
|
420 param(parm);
|
Chris@16
|
421 }
|
Chris@16
|
422
|
Chris@16
|
423 /**
|
Chris@16
|
424 * Returns a value distributed according to the parameters of the
|
Chris@16
|
425 * discrete_distribution.
|
Chris@16
|
426 */
|
Chris@16
|
427 template<class URNG>
|
Chris@16
|
428 IntType operator()(URNG& urng) const
|
Chris@16
|
429 {
|
Chris@101
|
430 BOOST_ASSERT(!_impl._alias_table.empty());
|
Chris@101
|
431 IntType result;
|
Chris@101
|
432 WeightType test;
|
Chris@101
|
433 do {
|
Chris@101
|
434 result = uniform_int_distribution<IntType>((min)(), (max)())(urng);
|
Chris@101
|
435 test = _impl.test(urng);
|
Chris@101
|
436 } while(!_impl.accept(result, test));
|
Chris@101
|
437 if(test < _impl._alias_table[result].first) {
|
Chris@16
|
438 return result;
|
Chris@16
|
439 } else {
|
Chris@101
|
440 return(_impl._alias_table[result].second);
|
Chris@16
|
441 }
|
Chris@16
|
442 }
|
Chris@16
|
443
|
Chris@16
|
444 /**
|
Chris@16
|
445 * Returns a value distributed according to the parameters
|
Chris@16
|
446 * specified by param.
|
Chris@16
|
447 */
|
Chris@16
|
448 template<class URNG>
|
Chris@16
|
449 IntType operator()(URNG& urng, const param_type& parm) const
|
Chris@16
|
450 {
|
Chris@101
|
451 if(WeightType limit = impl_type::try_get_sum(parm._probabilities)) {
|
Chris@101
|
452 WeightType val = impl_type::generate_in_range(urng, limit);
|
Chris@16
|
453 WeightType sum = 0;
|
Chris@16
|
454 std::size_t result = 0;
|
Chris@16
|
455 for(typename std::vector<WeightType>::const_iterator
|
Chris@101
|
456 iter = parm._probabilities.begin(),
|
Chris@101
|
457 end = parm._probabilities.end();
|
Chris@16
|
458 iter != end; ++iter, ++result)
|
Chris@16
|
459 {
|
Chris@16
|
460 sum += *iter;
|
Chris@16
|
461 if(sum > val) {
|
Chris@16
|
462 return result;
|
Chris@16
|
463 }
|
Chris@16
|
464 }
|
Chris@101
|
465 // This shouldn't be reachable, but round-off error
|
Chris@101
|
466 // can prevent any match from being found when val is
|
Chris@101
|
467 // very close to 1.
|
Chris@101
|
468 return static_cast<IntType>(parm._probabilities.size() - 1);
|
Chris@101
|
469 } else {
|
Chris@101
|
470 // WeightType is integral and sum(parm._probabilities)
|
Chris@101
|
471 // would overflow. Just use the easy solution.
|
Chris@101
|
472 return discrete_distribution(parm)(urng);
|
Chris@16
|
473 }
|
Chris@16
|
474 }
|
Chris@16
|
475
|
Chris@16
|
476 /** Returns the smallest value that the distribution can produce. */
|
Chris@16
|
477 result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return 0; }
|
Chris@16
|
478 /** Returns the largest value that the distribution can produce. */
|
Chris@16
|
479 result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const
|
Chris@101
|
480 { return static_cast<result_type>(_impl._alias_table.size() - 1); }
|
Chris@16
|
481
|
Chris@16
|
482 /**
|
Chris@16
|
483 * Returns a vector containing the probabilities of each
|
Chris@16
|
484 * value of the distribution. For example, given
|
Chris@16
|
485 *
|
Chris@16
|
486 * @code
|
Chris@16
|
487 * discrete_distribution<> dist = { 1, 4, 5 };
|
Chris@16
|
488 * std::vector<double> p = dist.param();
|
Chris@16
|
489 * @endcode
|
Chris@16
|
490 *
|
Chris@16
|
491 * the vector, p will contain {0.1, 0.4, 0.5}.
|
Chris@101
|
492 *
|
Chris@101
|
493 * If @c WeightType is integral, then the weights
|
Chris@101
|
494 * will be returned unchanged.
|
Chris@16
|
495 */
|
Chris@16
|
496 std::vector<WeightType> probabilities() const
|
Chris@16
|
497 {
|
Chris@101
|
498 std::vector<WeightType> result(_impl._alias_table.size());
|
Chris@16
|
499 std::size_t i = 0;
|
Chris@101
|
500 for(typename impl_type::alias_table_t::const_iterator
|
Chris@101
|
501 iter = _impl._alias_table.begin(),
|
Chris@101
|
502 end = _impl._alias_table.end();
|
Chris@16
|
503 iter != end; ++iter, ++i)
|
Chris@16
|
504 {
|
Chris@101
|
505 WeightType val = iter->first;
|
Chris@16
|
506 result[i] += val;
|
Chris@101
|
507 result[iter->second] += _impl.get_weight(i) - val;
|
Chris@16
|
508 }
|
Chris@101
|
509 impl_type::normalize(result);
|
Chris@16
|
510 return(result);
|
Chris@16
|
511 }
|
Chris@16
|
512
|
Chris@16
|
513 /** Returns the parameters of the distribution. */
|
Chris@16
|
514 param_type param() const
|
Chris@16
|
515 {
|
Chris@16
|
516 return param_type(*this);
|
Chris@16
|
517 }
|
Chris@16
|
518 /** Sets the parameters of the distribution. */
|
Chris@16
|
519 void param(const param_type& parm)
|
Chris@16
|
520 {
|
Chris@16
|
521 init(parm._probabilities.begin(), parm._probabilities.end());
|
Chris@16
|
522 }
|
Chris@16
|
523
|
Chris@16
|
524 /**
|
Chris@16
|
525 * Effects: Subsequent uses of the distribution do not depend
|
Chris@16
|
526 * on values produced by any engine prior to invoking reset.
|
Chris@16
|
527 */
|
Chris@16
|
528 void reset() {}
|
Chris@16
|
529
|
Chris@16
|
530 /** Writes a distribution to a @c std::ostream. */
|
Chris@16
|
531 BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, discrete_distribution, dd)
|
Chris@16
|
532 {
|
Chris@16
|
533 os << dd.param();
|
Chris@16
|
534 return os;
|
Chris@16
|
535 }
|
Chris@16
|
536
|
Chris@16
|
537 /** Reads a distribution from a @c std::istream */
|
Chris@16
|
538 BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, discrete_distribution, dd)
|
Chris@16
|
539 {
|
Chris@16
|
540 param_type parm;
|
Chris@16
|
541 if(is >> parm) {
|
Chris@16
|
542 dd.param(parm);
|
Chris@16
|
543 }
|
Chris@16
|
544 return is;
|
Chris@16
|
545 }
|
Chris@16
|
546
|
Chris@16
|
547 /**
|
Chris@16
|
548 * Returns true if the two distributions will return the
|
Chris@16
|
549 * same sequence of values, when passed equal generators.
|
Chris@16
|
550 */
|
Chris@16
|
551 BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(discrete_distribution, lhs, rhs)
|
Chris@16
|
552 {
|
Chris@101
|
553 return lhs._impl == rhs._impl;
|
Chris@16
|
554 }
|
Chris@16
|
555 /**
|
Chris@16
|
556 * Returns true if the two distributions may return different
|
Chris@16
|
557 * sequences of values, when passed equal generators.
|
Chris@16
|
558 */
|
Chris@16
|
559 BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(discrete_distribution)
|
Chris@16
|
560
|
Chris@16
|
561 private:
|
Chris@16
|
562
|
Chris@16
|
563 /// @cond show_private
|
Chris@16
|
564
|
Chris@16
|
565 template<class Iter>
|
Chris@16
|
566 void init(Iter first, Iter last, std::input_iterator_tag)
|
Chris@16
|
567 {
|
Chris@16
|
568 std::vector<WeightType> temp(first, last);
|
Chris@16
|
569 init(temp.begin(), temp.end());
|
Chris@16
|
570 }
|
Chris@16
|
571 template<class Iter>
|
Chris@16
|
572 void init(Iter first, Iter last, std::forward_iterator_tag)
|
Chris@16
|
573 {
|
Chris@16
|
574 std::vector<std::pair<WeightType, IntType> > below_average;
|
Chris@16
|
575 std::vector<std::pair<WeightType, IntType> > above_average;
|
Chris@101
|
576 WeightType weight_average = _impl.init_average(first, last);
|
Chris@101
|
577 WeightType normalized_average = _impl.get_weight(0);
|
Chris@16
|
578 std::size_t i = 0;
|
Chris@16
|
579 for(; first != last; ++first, ++i) {
|
Chris@101
|
580 WeightType val = impl_type::normalize(*first, weight_average);
|
Chris@16
|
581 std::pair<WeightType, IntType> elem(val, static_cast<IntType>(i));
|
Chris@101
|
582 if(val < normalized_average) {
|
Chris@16
|
583 below_average.push_back(elem);
|
Chris@16
|
584 } else {
|
Chris@16
|
585 above_average.push_back(elem);
|
Chris@16
|
586 }
|
Chris@16
|
587 }
|
Chris@16
|
588
|
Chris@101
|
589 typename impl_type::alias_table_t::iterator
|
Chris@16
|
590 b_iter = below_average.begin(),
|
Chris@16
|
591 b_end = below_average.end(),
|
Chris@16
|
592 a_iter = above_average.begin(),
|
Chris@16
|
593 a_end = above_average.end()
|
Chris@16
|
594 ;
|
Chris@16
|
595 while(b_iter != b_end && a_iter != a_end) {
|
Chris@101
|
596 _impl._alias_table[b_iter->second] =
|
Chris@16
|
597 std::make_pair(b_iter->first, a_iter->second);
|
Chris@101
|
598 a_iter->first -= (_impl.get_weight(b_iter->second) - b_iter->first);
|
Chris@101
|
599 if(a_iter->first < normalized_average) {
|
Chris@16
|
600 *b_iter = *a_iter++;
|
Chris@16
|
601 } else {
|
Chris@16
|
602 ++b_iter;
|
Chris@16
|
603 }
|
Chris@16
|
604 }
|
Chris@16
|
605 for(; b_iter != b_end; ++b_iter) {
|
Chris@101
|
606 _impl._alias_table[b_iter->second].first =
|
Chris@101
|
607 _impl.get_weight(b_iter->second);
|
Chris@16
|
608 }
|
Chris@16
|
609 for(; a_iter != a_end; ++a_iter) {
|
Chris@101
|
610 _impl._alias_table[a_iter->second].first =
|
Chris@101
|
611 _impl.get_weight(a_iter->second);
|
Chris@16
|
612 }
|
Chris@16
|
613 }
|
Chris@16
|
614 template<class Iter>
|
Chris@16
|
615 void init(Iter first, Iter last)
|
Chris@16
|
616 {
|
Chris@16
|
617 if(first == last) {
|
Chris@101
|
618 _impl.init_empty();
|
Chris@16
|
619 } else {
|
Chris@16
|
620 typename std::iterator_traits<Iter>::iterator_category category;
|
Chris@16
|
621 init(first, last, category);
|
Chris@16
|
622 }
|
Chris@16
|
623 }
|
Chris@101
|
624 typedef typename detail::select_alias_table<
|
Chris@101
|
625 (::boost::is_integral<WeightType>::value)
|
Chris@101
|
626 >::template apply<IntType, WeightType>::type impl_type;
|
Chris@101
|
627 impl_type _impl;
|
Chris@16
|
628 /// @endcond
|
Chris@16
|
629 };
|
Chris@16
|
630
|
Chris@16
|
631 }
|
Chris@16
|
632 }
|
Chris@16
|
633
|
Chris@16
|
634 #include <boost/random/detail/enable_warnings.hpp>
|
Chris@16
|
635
|
Chris@16
|
636 #endif
|