random_number_generator.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. // Copyright (C) 2010 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or distribute this software for any
  4. // purpose with or without fee is hereby granted, provided that the above
  5. // copyright notice and this permission notice appear in all copies.
  6. //
  7. // THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef NSAS_RANDOM_NUMBER_GENERATOR_H
  15. #define NSAS_RANDOM_NUMBER_GENERATOR_H
  16. #include <algorithm>
  17. #include <cmath>
  18. #include <numeric>
  19. #include <vector>
  20. #include <exceptions/exceptions.h>
  21. #include <boost/random/mersenne_twister.hpp>
  22. #include <boost/random/uniform_int.hpp>
  23. #include <boost/random/uniform_real.hpp>
  24. #include <boost/random/variate_generator.hpp>
  25. namespace isc {
  26. namespace util {
  27. namespace random {
  28. class InvalidLimits : public isc::BadValue {
  29. public:
  30. InvalidLimits(const char* file, size_t line, const char* what) :
  31. isc::BadValue(file, line, what) {}
  32. };
  33. class SumNotOne : public isc::BadValue {
  34. public:
  35. SumNotOne(const char* file, size_t line, const char* what) :
  36. isc::BadValue(file, line, what) {}
  37. };
  38. class InvalidProbValue : public isc::BadValue {
  39. public:
  40. InvalidProbValue(const char* file, size_t line, const char* what) :
  41. isc::BadValue(file, line, what) {}
  42. };
  43. /// \brief Uniform random integer generator
  44. ///
  45. /// Generate uniformly distributed integers in range of [min, max]
  46. class UniformRandomIntegerGenerator{
  47. public:
  48. /// \brief Constructor
  49. ///
  50. /// \param min The minimum number in the range
  51. /// \param max The maximum number in the range
  52. UniformRandomIntegerGenerator(int min, int max):
  53. min_(std::min(min, max)), max_(std::max(min, max)),
  54. dist_(min_, max_), generator_(rng_, dist_)
  55. {
  56. // To preserve the restriction of the underlying uniform_int class (and
  57. // to retain compatibility with earlier versions of the class), we will
  58. // abort if the minimum and maximum given are the wrong way round.
  59. if (min > max) {
  60. isc_throw(InvalidLimits, "minimum limit is greater than maximum "
  61. "when initializing UniformRandomIntegerGenerator");
  62. }
  63. // Init with the current time
  64. rng_.seed(time(NULL));
  65. }
  66. /// \brief Generate uniformly distributed integer
  67. int operator()() { return generator_(); }
  68. private:
  69. /// Hide default and copy constructor
  70. UniformRandomIntegerGenerator();///< Default constructor
  71. UniformRandomIntegerGenerator(const UniformRandomIntegerGenerator&); ///< Copy constructor
  72. int min_; ///< The minimum integer that can generate
  73. int max_; ///< The maximum integer that can generate
  74. boost::uniform_int<> dist_; ///< Distribute uniformly.
  75. boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
  76. boost::variate_generator<boost::mt19937&, boost::uniform_int<> > generator_; ///< Uniform generator
  77. };
  78. /// \brief Weighted random integer generator
  79. ///
  80. /// Generate random integers according different probabilities
  81. class WeightedRandomIntegerGenerator {
  82. public:
  83. /// \brief Constructor
  84. ///
  85. /// \param probabilities The probabies for all the integers, the probability must be
  86. /// between 0 and 1.0, the sum of probabilities must be equal to 1.
  87. /// For example, if the probabilities contains the following values:
  88. /// 0.5 0.3 0.2, the 1st integer will be generated more frequently than the
  89. /// other integers and the probability is proportional to its value.
  90. /// \param min The minimum integer that generated, other integers will be
  91. /// min, min + 1, ..., min + probabilities.size() - 1
  92. WeightedRandomIntegerGenerator(const std::vector<double>& probabilities,
  93. size_t min = 0):
  94. dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(min)
  95. {
  96. // The probabilities must be valid. Checking is quite an expensive
  97. // operation, so is only done in a debug build.
  98. assert(areProbabilitiesValid(probabilities));
  99. // Calculate the partial sum of probabilities
  100. std::partial_sum(probabilities.begin(), probabilities.end(),
  101. std::back_inserter(cumulative_));
  102. // Init with the current time
  103. rng_.seed(time(NULL));
  104. }
  105. /// \brief Default constructor
  106. ///
  107. WeightedRandomIntegerGenerator():
  108. dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(0)
  109. {
  110. }
  111. /// \brief Reset the probabilities
  112. ///
  113. /// Change the weights of each integers
  114. /// \param probabilities The probabies for all the integers
  115. /// \param min The minimum integer that generated
  116. void reset(const std::vector<double>& probabilities, size_t min = 0)
  117. {
  118. // The probabilities must be valid.
  119. assert(areProbabilitiesValid(probabilities));
  120. // Reset the cumulative sum
  121. cumulative_.clear();
  122. // Calculate the partial sum of probabilities
  123. std::partial_sum(probabilities.begin(), probabilities.end(),
  124. std::back_inserter(cumulative_));
  125. // Reset the minimum integer
  126. min_ = min;
  127. }
  128. /// \brief Generate weighted random integer
  129. size_t operator()()
  130. {
  131. return std::lower_bound(cumulative_.begin(), cumulative_.end(), uniform_real_gen_())
  132. - cumulative_.begin() + min_;
  133. }
  134. private:
  135. /// \brief Check the validation of probabilities vector
  136. ///
  137. /// The probability must be in range of [0, 1.0] and the sum must be equal
  138. /// to 1.0. Empty probabilities are also valid.
  139. ///
  140. /// Checking the probabilities is quite an expensive operation, so it is
  141. /// only done during a debug build (via a call through assert()). However,
  142. /// instead of letting assert() call abort(), if this method encounters an
  143. /// error, an exception is thrown. This makes unit testing somewhat easier.
  144. ///
  145. /// \param probabilities Vector of probabilities.
  146. bool areProbabilitiesValid(const std::vector<double>& probabilities) const
  147. {
  148. typedef std::vector<double>::const_iterator Iterator;
  149. double sum = probabilities.empty() ? 1.0 : 0.0;
  150. for(Iterator it = probabilities.begin(); it != probabilities.end(); ++it){
  151. //The probability must be in [0, 1.0]
  152. if(*it < 0.0 || *it > 1.0) {
  153. isc_throw(InvalidProbValue,
  154. "probability must be in the range 0..1");
  155. }
  156. sum += *it;
  157. }
  158. double epsilon = 0.0001;
  159. // The sum must be equal to 1
  160. if (std::fabs(sum - 1.0) >= epsilon) {
  161. isc_throw(SumNotOne, "Sum of probabilities is not equal to 1");
  162. }
  163. return true;
  164. }
  165. std::vector<double> cumulative_; ///< Partial sum of the probabilities
  166. boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
  167. boost::uniform_real<> dist_; ///< Uniformly distributed real numbers
  168. // Shortcut typedef
  169. // This typedef is placed directly before its use, as the sunstudio
  170. // compiler could not handle it being anywhere else (don't know why)
  171. typedef boost::variate_generator<boost::mt19937&, boost::uniform_real<> > UniformRealGenerator;
  172. UniformRealGenerator uniform_real_gen_; ///< Uniformly distributed random real numbers generator
  173. size_t min_; ///< The minimum integer that will be generated
  174. };
  175. } // namespace random
  176. } // namespace util
  177. } // namespace isc
  178. #endif//NSAS_RANDOM_NUMBER_GENERATOR_H