uniform_smallint.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. /* boost random/uniform_smallint.hpp header file
  2. *
  3. * Copyright Jens Maurer 2000-2001
  4. * Distributed under the Boost Software License, Version 1.0. (See
  5. * accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. *
  8. * See http://www.boost.org for most recent version including documentation.
  9. *
  10. * $Id$
  11. *
  12. * Revision history
  13. * 2001-04-08 added min<max assertion (N. Becker)
  14. * 2001-02-18 moved to individual header files
  15. */
  16. #ifndef BOOST_RANDOM_UNIFORM_SMALLINT_HPP
  17. #define BOOST_RANDOM_UNIFORM_SMALLINT_HPP
  18. #include <istream>
  19. #include <iosfwd>
  20. #include <boost/assert.hpp>
  21. #include <boost/config.hpp>
  22. #include <boost/limits.hpp>
  23. #include <boost/type_traits/is_integral.hpp>
  24. #include <boost/random/detail/config.hpp>
  25. #include <boost/random/detail/operators.hpp>
  26. #include <boost/random/detail/signed_unsigned_tools.hpp>
  27. #include <boost/random/uniform_01.hpp>
  28. #include <boost/detail/workaround.hpp>
  29. #ifdef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
  30. #include <boost/type_traits/conditional.hpp>
  31. #endif
  32. namespace boost {
  33. namespace random {
  34. // uniform integer distribution on a small range [min, max]
  35. /**
  36. * The distribution function uniform_smallint models a \random_distribution.
  37. * On each invocation, it returns a random integer value uniformly distributed
  38. * in the set of integer numbers {min, min+1, min+2, ..., max}. It assumes
  39. * that the desired range (max-min+1) is small compared to the range of the
  40. * underlying source of random numbers and thus makes no attempt to limit
  41. * quantization errors.
  42. *
  43. * Let \f$r_{\mathtt{out}} = (\mbox{max}-\mbox{min}+1)\f$ the desired range of
  44. * integer numbers, and
  45. * let \f$r_{\mathtt{base}}\f$ be the range of the underlying source of random
  46. * numbers. Then, for the uniform distribution, the theoretical probability
  47. * for any number i in the range \f$r_{\mathtt{out}}\f$ will be
  48. * \f$\displaystyle p_{\mathtt{out}}(i) = \frac{1}{r_{\mathtt{out}}}\f$.
  49. * Likewise, assume a uniform distribution on \f$r_{\mathtt{base}}\f$ for
  50. * the underlying source of random numbers, i.e.
  51. * \f$\displaystyle p_{\mathtt{base}}(i) = \frac{1}{r_{\mathtt{base}}}\f$.
  52. * Let \f$p_{\mathtt{out\_s}}(i)\f$ denote the random
  53. * distribution generated by @c uniform_smallint. Then the sum over all
  54. * i in \f$r_{\mathtt{out}}\f$ of
  55. * \f$\displaystyle
  56. * \left(\frac{p_{\mathtt{out\_s}}(i)}{p_{\mathtt{out}}(i)} - 1\right)^2\f$
  57. * shall not exceed
  58. * \f$\displaystyle \frac{r_{\mathtt{out}}}{r_{\mathtt{base}}^2}
  59. * (r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})
  60. * (r_{\mathtt{out}} - r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})\f$.
  61. *
  62. * The template parameter IntType shall denote an integer-like value type.
  63. *
  64. * @xmlnote
  65. * The property above is the square sum of the relative differences
  66. * in probabilities between the desired uniform distribution
  67. * \f$p_{\mathtt{out}}(i)\f$ and the generated distribution
  68. * \f$p_{\mathtt{out\_s}}(i)\f$.
  69. * The property can be fulfilled with the calculation
  70. * \f$(\mbox{base\_rng} \mbox{ mod } r_{\mathtt{out}})\f$, as follows:
  71. * Let \f$r = r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}}\f$.
  72. * The base distribution on \f$r_{\mathtt{base}}\f$ is folded onto the
  73. * range \f$r_{\mathtt{out}}\f$. The numbers i < r have assigned
  74. * \f$\displaystyle
  75. * \left\lfloor\frac{r_{\mathtt{base}}}{r_{\mathtt{out}}}\right\rfloor+1\f$
  76. * numbers of the base distribution, the rest has only \f$\displaystyle
  77. * \left\lfloor\frac{r_{\mathtt{base}}}{r_{\mathtt{out}}}\right\rfloor\f$.
  78. * Therefore,
  79. * \f$\displaystyle p_{\mathtt{out\_s}}(i) =
  80. * \left(\left\lfloor\frac{r_{\mathtt{base}}}
  81. * {r_{\mathtt{out}}}\right\rfloor+1\right) /
  82. * r_{\mathtt{base}}\f$ for i < r and \f$\displaystyle p_{\mathtt{out\_s}}(i) =
  83. * \left\lfloor\frac{r_{\mathtt{base}}}
  84. * {r_{\mathtt{out}}}\right\rfloor/r_{\mathtt{base}}\f$ otherwise.
  85. * Substituting this in the
  86. * above sum formula leads to the desired result.
  87. * @endxmlnote
  88. *
  89. * Note: The upper bound for
  90. * \f$(r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})
  91. * (r_{\mathtt{out}} - r_{\mathtt{base}} \mbox{ mod } r_{\mathtt{out}})\f$ is
  92. * \f$\displaystyle \frac{r_{\mathtt{out}}^2}{4}\f$. Regarding the upper bound
  93. * for the square sum of the relative quantization error of
  94. * \f$\displaystyle \frac{r_\mathtt{out}^3}{4r_{\mathtt{base}}^2}\f$, it
  95. * seems wise to either choose \f$r_{\mathtt{base}}\f$ so that
  96. * \f$r_{\mathtt{base}} > 10r_{\mathtt{out}}^2\f$ or ensure that
  97. * \f$r_{\mathtt{base}}\f$ is
  98. * divisible by \f$r_{\mathtt{out}}\f$.
  99. */
  100. template<class IntType = int>
  101. class uniform_smallint
  102. {
  103. public:
  104. typedef IntType input_type;
  105. typedef IntType result_type;
  106. class param_type
  107. {
  108. public:
  109. typedef uniform_smallint distribution_type;
  110. /** constructs the parameters of a @c uniform_smallint distribution. */
  111. param_type(IntType min_arg = 0, IntType max_arg = 9)
  112. : _min(min_arg), _max(max_arg)
  113. {
  114. BOOST_ASSERT(_min <= _max);
  115. }
  116. /** Returns the minimum value. */
  117. IntType a() const { return _min; }
  118. /** Returns the maximum value. */
  119. IntType b() const { return _max; }
  120. /** Writes the parameters to a @c std::ostream. */
  121. BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm)
  122. {
  123. os << parm._min << " " << parm._max;
  124. return os;
  125. }
  126. /** Reads the parameters from a @c std::istream. */
  127. BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm)
  128. {
  129. is >> parm._min >> std::ws >> parm._max;
  130. return is;
  131. }
  132. /** Returns true if the two sets of parameters are equal. */
  133. BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs)
  134. { return lhs._min == rhs._min && lhs._max == rhs._max; }
  135. /** Returns true if the two sets of parameters are different. */
  136. BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type)
  137. private:
  138. IntType _min;
  139. IntType _max;
  140. };
  141. /**
  142. * Constructs a @c uniform_smallint. @c min and @c max are the
  143. * lower and upper bounds of the output range, respectively.
  144. */
  145. explicit uniform_smallint(IntType min_arg = 0, IntType max_arg = 9)
  146. : _min(min_arg), _max(max_arg) {}
  147. /**
  148. * Constructs a @c uniform_smallint from its parameters.
  149. */
  150. explicit uniform_smallint(const param_type& parm)
  151. : _min(parm.a()), _max(parm.b()) {}
  152. /** Returns the minimum value of the distribution. */
  153. result_type a() const { return _min; }
  154. /** Returns the maximum value of the distribution. */
  155. result_type b() const { return _max; }
  156. /** Returns the minimum value of the distribution. */
  157. result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _min; }
  158. /** Returns the maximum value of the distribution. */
  159. result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _max; }
  160. /** Returns the parameters of the distribution. */
  161. param_type param() const { return param_type(_min, _max); }
  162. /** Sets the parameters of the distribution. */
  163. void param(const param_type& parm)
  164. {
  165. _min = parm.a();
  166. _max = parm.b();
  167. }
  168. /**
  169. * Effects: Subsequent uses of the distribution do not depend
  170. * on values produced by any engine prior to invoking reset.
  171. */
  172. void reset() { }
  173. /** Returns a value uniformly distributed in the range [min(), max()]. */
  174. template<class Engine>
  175. result_type operator()(Engine& eng) const
  176. {
  177. typedef typename Engine::result_type base_result;
  178. return generate(eng, boost::random::traits::is_integral<base_result>());
  179. }
  180. /** Returns a value uniformly distributed in the range [param.a(), param.b()]. */
  181. template<class Engine>
  182. result_type operator()(Engine& eng, const param_type& parm) const
  183. { return uniform_smallint(parm)(eng); }
  184. /** Writes the distribution to a @c std::ostream. */
  185. BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, uniform_smallint, ud)
  186. {
  187. os << ud._min << " " << ud._max;
  188. return os;
  189. }
  190. /** Reads the distribution from a @c std::istream. */
  191. BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, uniform_smallint, ud)
  192. {
  193. is >> ud._min >> std::ws >> ud._max;
  194. return is;
  195. }
  196. /**
  197. * Returns true if the two distributions will produce identical
  198. * sequences of values given equal generators.
  199. */
  200. BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(uniform_smallint, lhs, rhs)
  201. { return lhs._min == rhs._min && lhs._max == rhs._max; }
  202. /**
  203. * Returns true if the two distributions may produce different
  204. * sequences of values given equal generators.
  205. */
  206. BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(uniform_smallint)
  207. private:
  208. // \cond show_private
  209. template<class Engine>
  210. result_type generate(Engine& eng, boost::true_type) const
  211. {
  212. // equivalent to (eng() - eng.min()) % (_max - _min + 1) + _min,
  213. // but guarantees no overflow.
  214. typedef typename Engine::result_type base_result;
  215. typedef typename boost::random::traits::make_unsigned<base_result>::type base_unsigned;
  216. typedef typename boost::random::traits::make_unsigned_or_unbounded<result_type>::type range_type;
  217. #ifdef BOOST_NO_CXX11_EXPLICIT_CONVERSION_OPERATORS
  218. typedef typename conditional<
  219. std::numeric_limits<range_type>::is_specialized && std::numeric_limits<base_unsigned>::is_specialized
  220. && (std::numeric_limits<range_type>::digits >= std::numeric_limits<base_unsigned>::digits),
  221. range_type, base_unsigned>::type mixed_range_type;
  222. #else
  223. typedef base_unsigned mixed_range_type;
  224. #endif
  225. range_type range = random::detail::subtract<result_type>()(_max, _min);
  226. base_unsigned base_range =
  227. random::detail::subtract<base_result>()((eng.max)(), (eng.min)());
  228. base_unsigned val =
  229. random::detail::subtract<base_result>()(eng(), (eng.min)());
  230. if(range >= base_range) {
  231. return boost::random::detail::add<range_type, result_type>()(
  232. static_cast<range_type>(val), _min);
  233. } else {
  234. // This involves mixed arithmetic between the base generators range
  235. // type, and the result_type's range type. mixed_range_type is
  236. // normally the same as base_unsigned which is the most efficient
  237. // option, but requires a narrowing explcit cast if result_type
  238. // is a multiprecision type. If no such casts are available then use
  239. // multiprecision arithmetic throughout instead.
  240. mixed_range_type modulus = static_cast<mixed_range_type>(range)+1;
  241. return boost::random::detail::add<range_type, result_type>()(
  242. static_cast<mixed_range_type>(val) % modulus, _min);
  243. }
  244. }
  245. template<class Engine>
  246. result_type generate(Engine& eng, boost::false_type) const
  247. {
  248. typedef typename Engine::result_type base_result;
  249. typedef typename boost::random::traits::make_unsigned<result_type>::type range_type;
  250. range_type range = random::detail::subtract<result_type>()(_max, _min);
  251. base_result val = boost::uniform_01<base_result>()(eng);
  252. // what is the worst that can possibly happen here?
  253. // base_result may not be able to represent all the values in [0, range]
  254. // exactly. If this happens, it will cause round off error and we
  255. // won't be able to produce all the values in the range. We don't
  256. // care about this because the user has already told us not to by
  257. // using uniform_smallint. However, we do need to be careful
  258. // to clamp the result, or floating point rounding can produce
  259. // an out of range result.
  260. range_type offset = static_cast<range_type>(val * (static_cast<base_result>(range) + 1));
  261. if(offset > range) return _max;
  262. return boost::random::detail::add<range_type, result_type>()(offset , _min);
  263. }
  264. // \endcond
  265. result_type _min;
  266. result_type _max;
  267. };
  268. } // namespace random
  269. using random::uniform_smallint;
  270. } // namespace boost
  271. #endif // BOOST_RANDOM_UNIFORM_SMALLINT_HPP