random_number_generator.h 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  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 <exceptions/exceptions.h>
  20. #include <boost/random/mersenne_twister.hpp>
  21. #include <boost/random/uniform_int.hpp>
  22. #include <boost/random/uniform_real.hpp>
  23. #include <boost/random/variate_generator.hpp>
  24. namespace isc {
  25. namespace nsas {
  26. class InvalidLimits : public Exception {
  27. public:
  28. InvalidLimits(const char* file, size_t line, const char* what) :
  29. isc::Exception(file, line, what) {}
  30. };
  31. class SumNotOne : public Exception {
  32. public:
  33. SumNotOne(const char* file, size_t line, const char* what) :
  34. isc::Exception(file, line, what) {}
  35. };
  36. class InvalidProbValue : public Exception {
  37. public:
  38. InvalidProbValue(const char* file, size_t line, const char* what) :
  39. isc::Exception(file, line, what) {}
  40. };
  41. /// \brief Uniform random integer generator
  42. ///
  43. /// Generate uniformly distributed integers in range of [min, max]
  44. class UniformRandomIntegerGenerator{
  45. public:
  46. /// \brief Constructor
  47. ///
  48. /// \param min The minimum number in the range
  49. /// \param max The maximum number in the range
  50. UniformRandomIntegerGenerator(int min, int max):
  51. min_(std::min(min, max)), max_(std::max(min, max)),
  52. dist_(min_, max_), generator_(rng_, dist_)
  53. {
  54. // To preserve the restriction of the underlying uniform_int class (and
  55. // to retain compatibility with earlier versions of the class), we will
  56. // abort if the minimum and maximum given are the wrong way round.
  57. if (min > max) {
  58. isc_throw(InvalidLimits, "minimum limit is greater than maximum "
  59. "when initializing UniformRandomIntegerGenerator");
  60. }
  61. // Init with the current time
  62. rng_.seed(time(NULL));
  63. }
  64. /// \brief Generate uniformly distributed integer
  65. int operator()() { return generator_(); }
  66. private:
  67. /// Hide default and copy constructor
  68. UniformRandomIntegerGenerator();///< Default constructor
  69. UniformRandomIntegerGenerator(const UniformRandomIntegerGenerator&); ///< Copy constructor
  70. int min_; ///< The minimum integer that can generate
  71. int max_; ///< The maximum integer that can generate
  72. boost::uniform_int<> dist_; ///< Distribute uniformly.
  73. boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
  74. boost::variate_generator<boost::mt19937&, boost::uniform_int<> > generator_; ///< Uniform generator
  75. };
  76. /// \brief Weighted random integer generator
  77. ///
  78. /// Generate random integers according different probabilities
  79. class WeightedRandomIntegerGenerator {
  80. public:
  81. /// \brief Constructor
  82. ///
  83. /// \param probabilities The probabies for all the integers, the probability must be
  84. /// between 0 and 1.0, the sum of probabilities must be equal to 1.
  85. /// For example, if the probabilities contains the following values:
  86. /// 0.5 0.3 0.2, the 1st integer will be generated more frequently than the
  87. /// other integers and the probability is proportional to its value.
  88. /// \param min The minimum integer that generated, other integers will be
  89. /// min, min + 1, ..., min + probabilities.size() - 1
  90. WeightedRandomIntegerGenerator(const std::vector<double>& probabilities,
  91. size_t min = 0):
  92. dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(min)
  93. {
  94. // The probabilities must be valid. Checking is quite an expensive
  95. // operation, so is only done in a debug build.
  96. assert(areProbabilitiesValid(probabilities));
  97. // Calculate the partial sum of probabilities
  98. std::partial_sum(probabilities.begin(), probabilities.end(),
  99. std::back_inserter(cumulative_));
  100. // Init with the current time
  101. rng_.seed(time(NULL));
  102. }
  103. /// \brief Default constructor
  104. ///
  105. WeightedRandomIntegerGenerator():
  106. dist_(0, 1.0), uniform_real_gen_(rng_, dist_), min_(0)
  107. {
  108. }
  109. /// \brief Reset the probabilities
  110. ///
  111. /// Change the weights of each integers
  112. /// \param probabilities The probabies for all the integers
  113. /// \param min The minimum integer that generated
  114. void reset(const std::vector<double>& probabilities, size_t min = 0)
  115. {
  116. // The probabilities must be valid.
  117. assert(areProbabilitiesValid(probabilities));
  118. // Reset the cumulative sum
  119. cumulative_.clear();
  120. // Calculate the partial sum of probabilities
  121. std::partial_sum(probabilities.begin(), probabilities.end(),
  122. std::back_inserter(cumulative_));
  123. // Reset the minimum integer
  124. min_ = min;
  125. }
  126. /// \brief Generate weighted random integer
  127. size_t operator()()
  128. {
  129. return std::lower_bound(cumulative_.begin(), cumulative_.end(), uniform_real_gen_())
  130. - cumulative_.begin() + min_;
  131. }
  132. private:
  133. /// \brief Check the validation of probabilities vector
  134. ///
  135. /// The probability must be in range of [0, 1.0] and the sum must be equal
  136. /// to 1.0. Empty probabilities are also valid.
  137. ///
  138. /// Checking the probabilities is quite an expensive operation, so it is
  139. /// only done during a debug build (via a call through assert()). However,
  140. /// instead of letting assert() call abort(), if this method encounters an
  141. /// error, an exception is thrown. This makes unit testing somewhat easier.
  142. ///
  143. /// \param probabilities Vector of probabilities.
  144. bool areProbabilitiesValid(const std::vector<double>& probabilities) const
  145. {
  146. typedef std::vector<double>::const_iterator Iterator;
  147. double sum = probabilities.empty() ? 1.0 : 0.0;
  148. for(Iterator it = probabilities.begin(); it != probabilities.end(); ++it){
  149. //The probability must be in [0, 1.0]
  150. if(*it < 0.0 || *it > 1.0) {
  151. isc_throw(InvalidProbValue,
  152. "probability must be in the range 0..1");
  153. }
  154. sum += *it;
  155. }
  156. double epsilon = 0.0001;
  157. // The sum must be equal to 1
  158. if (std::fabs(sum - 1.0) >= epsilon) {
  159. isc_throw(SumNotOne, "Sum of probabilities is not equal to 1");
  160. }
  161. return true;
  162. }
  163. std::vector<double> cumulative_; ///< Partial sum of the probabilities
  164. boost::mt19937 rng_; ///< Mersenne Twister: A 623-dimensionally equidistributed uniform pseudo-random number generator
  165. boost::uniform_real<> dist_; ///< Uniformly distributed real numbers
  166. // Shortcut typedef
  167. // This typedef is placed directly before its use, as the sunstudio
  168. // compiler could not handle it being anywhere else (don't know why)
  169. typedef boost::variate_generator<boost::mt19937&, boost::uniform_real<> > UniformRealGenerator;
  170. UniformRealGenerator uniform_real_gen_; ///< Uniformly distributed random real numbers generator
  171. size_t min_; ///< The minimum integer that will be generated
  172. };
  173. } // namespace dns
  174. } // namespace isc
  175. #endif//__NSAS_RANDOM_NUMBER_GENERATOR_H