/* boost random/uniform_smallint.hpp header file * * Copyright Jens Maurer 2000-2001 * Distributed under the Boost Software License, Version 1.0. (See * accompanying file LICENSE_1_0.txt or copy at * http://www.boost.org/LICENSE_1_0.txt) * * See http://www.boost.org for most recent version including documentation. * * $Id: uniform_smallint.hpp 60755 2010-03-22 00:45:06Z steven_watanabe $ * * Revision history * 2001-04-08 added min #include #include #include #include #include #include #include namespace boost { // uniform integer distribution on a small range [min, max] /** * The distribution function uniform_smallint models a \random_distribution. * On each invocation, it returns a random integer value uniformly distributed * in the set of integer numbers {min, min+1, min+2, ..., max}. It assumes * that the desired range (max-min+1) is small compared to the range of the * underlying source of random numbers and thus makes no attempt to limit * quantization errors. * * Let rout=(max-min+1) the desired range of integer numbers, and * let rbase be the range of the underlying source of random * numbers. Then, for the uniform distribution, the theoretical probability * for any number i in the range rout will be pout(i) = * 1/rout. Likewise, assume a uniform distribution on rbase for * the underlying source of random numbers, i.e. pbase(i) = * 1/rbase. Let pout_s(i) denote the random * distribution generated by @c uniform_smallint. Then the sum over all * i in rout of (pout_s(i)/pout(i) - 1)2 * shall not exceed rout/rbase2 * (rbase mod rout)(rout - * rbase mod rout). * * The template parameter IntType shall denote an integer-like value type. * * Note: The property above is the square sum of the relative differences * in probabilities between the desired uniform distribution * pout(i) and the generated distribution pout_s(i). * The property can be fulfilled with the calculation * (base_rng mod rout), as follows: Let r = rbase mod * rout. The base distribution on rbase is folded onto the * range rout. The numbers i < r have assigned (rbase * div rout)+1 numbers of the base distribution, the rest has * only (rbase div rout). Therefore, * pout_s(i) = ((rbase div rout)+1) / * rbase for i < r and pout_s(i) = (rbase * div rout)/rbase otherwise. Substituting this in the * above sum formula leads to the desired result. * * Note: The upper bound for (rbase mod rout) * (rout - rbase mod rout) is * rout2/4. Regarding the upper bound for the * square sum of the relative quantization error of * rout3/(4*rbase2), it * seems wise to either choose rbase so that rbase > * 10*rout2 or ensure that rbase is * divisible by rout. */ template class uniform_smallint { public: typedef IntType input_type; typedef IntType result_type; /** * Constructs a @c uniform_smallint. @c min and @c max are the * lower and upper bounds of the output range, respectively. */ explicit uniform_smallint(IntType min_arg = 0, IntType max_arg = 9) : _min(min_arg), _max(max_arg) { #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS // MSVC fails BOOST_STATIC_ASSERT with std::numeric_limits at class scope BOOST_STATIC_ASSERT(std::numeric_limits::is_integer); #endif } result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _min; } result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { return _max; } void reset() { } template result_type operator()(Engine& eng) { typedef typename Engine::result_type base_result; base_result _range = static_cast(_max-_min)+1; base_result _factor = 1; // LCGs get bad when only taking the low bits. // (probably put this logic into a partial template specialization) // Check how many low bits we can ignore before we get too much // quantization error. base_result r_base = (eng.max)() - (eng.min)(); if(r_base == (std::numeric_limits::max)()) { _factor = 2; r_base /= 2; } r_base += 1; if(r_base % _range == 0) { // No quantization effects, good _factor = r_base / _range; } else { // carefully avoid overflow; pessimizing here for( ; r_base/_range/32 >= _range; _factor *= 2) r_base /= 2; } return static_cast(((eng() - (eng.min)()) / _factor) % _range + _min); } #ifndef BOOST_RANDOM_NO_STREAM_OPERATORS template friend std::basic_ostream& operator<<(std::basic_ostream& os, const uniform_smallint& ud) { os << ud._min << " " << ud._max; return os; } template friend std::basic_istream& operator>>(std::basic_istream& is, uniform_smallint& ud) { is >> std::ws >> ud._min >> std::ws >> ud._max; return is; } #endif private: result_type _min; result_type _max; }; } // namespace boost #endif // BOOST_RANDOM_UNIFORM_SMALLINT_HPP