ExRandom  3.0
i_rand.hpp
Go to the documentation of this file.
1 /**
2  * @file i_rand.hpp
3  * @author Charles Karney <charles.karney@sri.com>
4  * @brief Definition of i_rand
5  *
6  * Copyright (c) Charles Karney (2014-2020) and licensed under the MIT/X11
7  * License. For more information, see http://exrandom.sourceforge.net/
8  */
9 
10 #if !defined(EXRANDOM_I_RAND_HPP)
11 #define EXRANDOM_I_RAND_HPP 1
12 
13 #include <string> // for print routines
14 #include <sstream> // for print routines
15 #include <iostream> // for std::ostream, etc.
16 
17 namespace exrandom {
18 
19  /**
20  * @brief A class to sample integers [0,m).
21  *
22  * The basic method for sampling is taken from J. Lumbroso (2013),
23  * <a href="https://arxiv.org/abs/1304.1916">arxiv:1304.1916</a> generalized
24  * to accept random digits in an arbitrary base, b. However, an important
25  * additional feature is that only sufficient random digits are drawn to
26  * narrow the allowed range to a power of b. Thus, for b = 2, after
27  * initializing with m = 9 the i_rand represents
28  *
29  * range prob
30  * [0,8) 32/63
31  * [0,2) 2/21
32  * [2,6) 4/21
33  * [6,8) 2/21
34  * [8,9) 1/9
35  *
36  * min() and max() give the current extent of the closed range. The number
37  * of additional random digits needed to fix the value is given by entropy().
38  * The comparison operations may require additional digits to be drawn and so
39  * the range might be narrowed down, e.g., to [4,8). If you need a definite
40  * value then use operator()().
41  *
42  * @tparam digit_gen the type of digit generator.
43  */
44  template<typename digit_gen> class i_rand {
45  public:
46  explicit
47  /**
48  * The constructor.
49  *
50  * @param D A reference to the digit generator to be used.
51  *
52  * Initializes to a random integer in [0,1), i.e., to 0.
53  */
54  i_rand(digit_gen& D)
55  : _a(0), _d(1), _l(0), _D(D) {}
56  /**
57  * Initialize.
58  *
59  * @tparam Generator the type of g.
60  * @param g the random generator engine.
61  * @param m this must be greater than 0.
62  * @return *this representing a integer uniform distributed in [0,m).
63  *
64  * If @e m is less than 1, it is treated as 1.
65  */
66  template<typename Generator>
67  i_rand& init(Generator& g, int m) {
68  if (m <= 0) m = 1;
69  for (long long v = 1, c = 0;;) {
70  _l = 0;
71  for (long long w = v, a = c, d = 1;;) {
72  // play out Lumbroso's algorithm without drawing random digits with w
73  // playing the role of v and c represented by the range [a, a + d).
74  // Return if both ends of range qualify as return values at the same
75  // time. Otherwise, fail and draw another random digit.
76  if (w >= m) {
77  long long j = (a / m) * m; a -= j; w -= j;
78  if (w >= m) {
79  if (a + d <= m) { _a = int(a); _d = int(d); return *this; }
80  break;
81  }
82  }
83  w *= b; a *= b; d *= b; ++_l;
84  }
85  long long j = (v / m) * m; v -= j; c -= j;
86  v *= b; c *= b; c += _D(g);
87  }
88  }
89  /**
90  * Sample enough digits to narrow the range to an integer.
91  *
92  * @tparam Generator the type of g.
93  * @param g the random generator engine.
94  * @return value of the random integer.
95  */
96  template<typename Generator>
97  int operator()(Generator& g)
98  { while (_l) refine(g); return _a; }
99  /**
100  * @return the current lower end of the range.
101  */
102  int min() const { return _a; }
103  /**
104  * @return the current upper end of the range.
105  */
106  int max() const { return _a + _d - 1; }
107  /**
108  * @return the number of digits needs to complete the sampling.
109  */
110  int entropy() const { return _l; }
111  /**
112  * Negate the range.
113  */
114  void negate() { _a = -max(); }
115  /**
116  * Add a constant to the range.
117  *
118  * @param c the constant to be added.
119  */
120  void add(int c) { _a += c; }
121  /**
122  * Test *this &lt; m/n.
123  *
124  * @tparam Generator the type of g.
125  * @param g the random generator engine.
126  * @param m the numerator of the fraction.
127  * @param n the denominate of the fraction (default value 1).
128  * @return *this &lt; m/n.
129  *
130  * Requires that n &gt; 0.
131  */
132  template<typename Generator>
133  bool less_than(Generator& g, long long m, long long n = 1) {
134  for (;;) {
135  if ( (n * max() < m)) return true;
136  if (!(n * min() < m)) return false;
137  refine(g);
138  }
139  }
140  /**
141  * Test *this &le; m/n.
142  *
143  * @tparam Generator the type of g.
144  * @param g the random generator engine.
145  * @param m the numerator of the fraction.
146  * @param n the denominator of the fraction (default value 1).
147  * @return *this &le; m/n.
148  *
149  * Requires that n &gt; 0.
150  */
151  template<typename Generator>
152  bool less_than_equal(Generator& g, long long m, long long n = 1)
153  { return less_than(g, m + 1, n); }
154  /**
155  * Test *this &gt; m/n.
156  *
157  * @tparam Generator the type of g.
158  * @param g the random generator engine.
159  * @param m the numerator of the fraction.
160  * @param n the denominator of the fraction (default value 1).
161  * @return *this &gt; m/n.
162  *
163  * Requires that n &gt; 0.
164  */
165  template<typename Generator>
166  bool greater_than(Generator& g, long long m, long long n = 1)
167  { return !less_than_equal(g, m, n); }
168  /**
169  * Test *this &ge; m/n.
170  *
171  * @tparam Generator the type of g.
172  * @param g the random generator engine.
173  * @param m the numerator of the fraction.
174  * @param n the denominator of the fraction (default value 1).
175  * @return *this &ge; m/n.
176  *
177  * Requires that n &gt; 0.
178  */
179  template<typename Generator>
180  bool greater_than_equal(Generator& g, long long m, long long n = 1)
181  { return !less_than(g, m, n); }
182  /**
183  * @return a string representing the range.
184  *
185  * If the entropy is zero, the number is printed. Otherwise the range is
186  * represented by min()+[0,max()-min()+1). (Note that max()-min()+1 is a
187  * power of the base.)
188  */
189  std::string print() const {
190  std::ostringstream os;
191  if (_l)
192  os << min() << "+[0," << max() - min() + 1 << ')';
193  else
194  os << min();
195  return os.str();
196  }
197  /**
198  * Refine the range by sampling an extra digit.
199  *
200  * @tparam Generator the type of g.
201  * @param g the random generator engine.
202  */
203  template<typename Generator>
204  void refine(Generator& g) {
205  if (_l > 0) { --_l; _d /= b; _a += _D(g) * _d; }
206  }
207 
208  /**
209  * Print an i_rand. Format is min+[0,max-min+1) unless the entropy is
210  * zero, in which case it's val.
211  *
212  * @param os an output stream.
213  * @param h an i_rand.
214  * @return os.
215  **********************************************************************/
216  friend std::ostream& operator<<(std::ostream& os, const i_rand& h)
217  { os << h.print(); return os; }
218 
219  private:
220  static const int b = digit_gen::base;
221  int _a, _d, _l; // current range is _a + [0, _d); _d = b^_l.
222  // Disable copy assignment
223  i_rand& operator=(const i_rand&);
224  digit_gen& _D;
225  };
226 
227 }
228 
229 #endif // EXRANDOM_I_RAND_HPP
int max() const
Definition: i_rand.hpp:106
bool greater_than(Generator &g, long long m, long long n=1)
Definition: i_rand.hpp:166
i_rand(digit_gen &D)
Definition: i_rand.hpp:54
void add(int c)
Definition: i_rand.hpp:120
std::string print() const
Definition: i_rand.hpp:189
A class to sample integers [0,m).
Definition: i_rand.hpp:44
friend std::ostream & operator<<(std::ostream &os, const i_rand &h)
Definition: i_rand.hpp:216
int min() const
Definition: i_rand.hpp:102
The common namespace.
Definition: aux_info.hpp:18
int operator()(Generator &g)
Definition: i_rand.hpp:97
void refine(Generator &g)
Definition: i_rand.hpp:204
i_rand & init(Generator &g, int m)
Definition: i_rand.hpp:67
bool less_than_equal(Generator &g, long long m, long long n=1)
Definition: i_rand.hpp:152
int entropy() const
Definition: i_rand.hpp:110
bool greater_than_equal(Generator &g, long long m, long long n=1)
Definition: i_rand.hpp:180
bool less_than(Generator &g, long long m, long long n=1)
Definition: i_rand.hpp:133