RandomLib  1.10
 All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Macros Pages
ExactExponential.hpp
Go to the documentation of this file.
1 /**
2  * \file ExactExponential.hpp
3  * \brief Header for ExactExponential
4  *
5  * Sample exactly from an exponential distribution.
6  *
7  * Copyright (c) Charles Karney (2006-2012) <charles@karney.com> and licensed
8  * under the MIT/X11 License. For more information, see
9  * http://randomlib.sourceforge.net/
10  **********************************************************************/
11 
12 #if !defined(RANDOMLIB_EXACTEXPONENTIAL_HPP)
13 #define RANDOMLIB_EXACTEXPONENTIAL_HPP 1
14 
16 
17 #if defined(_MSC_VER)
18 // Squelch warnings about constant conditional expressions
19 # pragma warning (push)
20 # pragma warning (disable: 4127)
21 #endif
22 
23 namespace RandomLib {
24  /**
25  * \brief Sample exactly from an exponential distribution.
26  *
27  * Sample \e x &ge; 0 from exp(&minus;\e x). See:
28  * - J. von Neumann, Various Techniques used in Connection with Random
29  * Digits, J. Res. Nat. Bur. Stand., Appl. Math. Ser. 12, 36--38
30  * (1951), reprinted in Collected Works, Vol. 5, 768--770 (Pergammon,
31  * 1963).
32  * - M. Abramowitz and I. A. Stegun, Handbook of Mathematical Functions
33  * (National Bureau of Standards, 1964), Sec. 26.8.6.c(2).
34  * - G. E. Forsythe, Von Neumann's Comparison Method for Random Sampling from
35  * Normal and Other Distributions, Math. Comp. 26, 817--826 (1972).
36  * - D. E. Knuth, TAOCP, Vol 2, Sec 3.4.1.C.3.
37  * - D. E. Knuth and A. C. Yao, The Complexity of Nonuniform Random Number
38  * Generation, in "Algorithms and Complexity" (Academic Press, 1976),
39  * pp. 357--428.
40  * - P. Flajolet and N. Saheb, The Complexity of Generating an
41  * Exponentially Distributed Variate, J. Algorithms 7, 463--488 (1986).
42  *
43  * The following code illustrates the basic method given by von Neumann:
44  * \code
45  // Return a random number x >= 0 distributed with probability exp(-x).
46  double ExpDist(RandomLib::Random& r) {
47  for (unsigned k = 0; ; ++k) {
48  double x = r.Fixed(), // executed 1/(1-exp(-1)) times on average
49  p = x, q;
50  do {
51  q = r.Fixed(); // executed exp(x)*cosh(x) times on average
52  if (!(q < p)) return k + x;
53  p = r.Fixed(); // executed exp(x)*sinh(x) times on average
54  } while (p < q);
55  }
56  }
57  \endcode
58  * This returns a result consuming exp(1)/(1 &minus; exp(-1)) = 4.30 random
59  * numbers on average. (Von Neumann incorrectly states that the method takes
60  * (1 + exp(1))/(1 &minus; exp(-1)) = 5.88 random numbers on average.)
61  * Because of the finite precision of Random::Fixed(), the code snippet above
62  * only approximates exp(&minus;\e x). Instead, it returns \e x with
63  * probability \e h(1 &minus; \e h)<sup><i>x</i>/<i>h</i></sup> for \e x = \e
64  * ih, \e h = 2<sup>&minus;53</sup>, and integer \e i &ge; 0.
65  *
66  * The above is precisely von Neumann's method. Abramowitz and Stegun
67  * consider a variant: sample uniform variants until the first is less than
68  * the sum of the rest. Forsythe converts the < ranking for the runs to &le;
69  * which makes the analysis of the discrete case more difficult. He also
70  * drops the "trick" by which the integer part of the deviate is given by the
71  * number of rejections when finding the fractional part.
72  *
73  * Von Neumann says of his method: "The machine has in effect computed a
74  * logarithm by performing only discriminations on the relative magnitude of
75  * numbers in (0,1). It is a sad fact of life, however, that under the
76  * particular conditions of the Eniac it was slightly quicker to use a
77  * truncated power series for log(1&minus;\e T) than to carry out all the
78  * discriminations."
79  *
80  * Here the code is modified to make it more efficient:
81  * \code
82  // Return a random number x >= 0 distributed with probability exp(-x).
83  double ExpDist(RandomLib::Random& r) {
84  for (unsigned k = 0; ; ++k) {
85  double x = r.Fixed(); // executed 1/(1-exp(-1/2)) times on average
86  if (x >= 0.5) continue;
87  double p = x, q;
88  do {
89  q = r.Fixed(); // executed exp(x)*cosh(x) times on average
90  if (!(q < p)) return 0.5 * k + x;
91  p = r.Fixed(); // executed exp(x)*sinh(x) times on average
92  } while (p < q);
93  }
94  }
95  \endcode
96  * In addition, the method is extended to use infinite precision uniform
97  * deviates implemented by RandomNumber and returning \e exact results for
98  * the exponential distribution. This is possible because only comparisons
99  * are done in the method. The template parameter \e bits specifies the
100  * number of bits in the base used for RandomNumber (i.e., base =
101  * 2<sup><i>bits</i></sup>).
102  *
103  * For example the following samples from an exponential distribution and
104  * prints various representations of the result.
105  * \code
106  #include <RandomLib/RandomNumber.hpp>
107  #include <RandomLib/ExactExponential.hpp>
108 
109  RandomLib::Random r;
110  const int bits = 1;
111  RandomLib::ExactExponential<bits> edist;
112  for (size_t i = 0; i < 10; ++i) {
113  RandomLib::RandomNumber<bits> x = edist(r); // Sample
114  std::pair<double, double> z = x.Range();
115  std::cout << x << " = " // Print in binary with ellipsis
116  << "(" << z.first << "," << z.second << ")"; // Print range
117  double v = x.Value<double>(r); // Round exactly to nearest double
118  std::cout << " = " << v << "\n";
119  }
120  \endcode
121  * Here's a possible result: \verbatim
122  0.0111... = (0.4375,0.5) = 0.474126
123  10.000... = (2,2.125) = 2.05196
124  1.00... = (1,1.25) = 1.05766
125  0.010... = (0.25,0.375) = 0.318289
126  10.1... = (2.5,3) = 2.8732
127  0.0... = (0,0.5) = 0.30753
128  0.101... = (0.625,0.75) = 0.697654
129  0.00... = (0,0.25) = 0.0969214
130  0.0... = (0,0.5) = 0.194053
131  0.11... = (0.75,1) = 0.867946 \endverbatim
132  * First number is in binary with ... indicating an infinite sequence of
133  * random bits. Second number gives the corresponding interval. Third
134  * number is the result of filling in the missing bits and rounding exactly
135  * to the nearest representable double.
136  *
137  * This class uses some mutable RandomNumber objects. So a single
138  * ExactExponential object cannot safely be used by multiple threads. In a
139  * multi-processing environment, each thread should use a thread-specific
140  * ExactExponential object. In addition, these should be invoked with
141  * thread-specific random generator objects.
142  *
143  * @tparam bits the number of bits in each digit.
144  **********************************************************************/
145  template<int bits = 1> class ExactExponential {
146  public:
147  /**
148  * Return a random deviate with an exponential distribution, exp(&minus;\e
149  * x) for \e x &ge; 0. Requires 7.232 bits per invocation for \e bits = 1.
150  * The average number of bits in the fraction = 1.743. The relative
151  * frequency of the results for the fractional part with \e bits = 1 is
152  * shown by the histogram
153  * \image html exphist.png
154  * The base of each rectangle gives the range represented by the
155  * corresponding binary number and the area is proportional to its
156  * frequency. A PDF version of this figure is given
157  * <a href="exphist.pdf">here</a>. This allows the figure to be magnified
158  * to show the rectangles for all binary numbers up to 9 bits. Note that
159  * this histogram was generated using an earlier version of
160  * ExactExponential (thru version 1.3) that implements von Neumann's
161  * original method. The histogram generated with the current version of
162  * ExactExponential is the same as this figure for \e u in [0, 1/2]. The
163  * histogram for \e u in [1/2, 1] is obtained by shifting and scaling the
164  * part for \e u in [0, 1/2] to fit under the exponential curve.
165  *
166  * Another way of assessing the efficiency of the algorithm is thru the
167  * mean value of the balance = (number of random bits consumed) &minus;
168  * (number of bits in the result). If we code the result in mixed Knuth
169  * and Yao's unary-binary notation (integer is given in unary, followed by
170  * "0" as a separator, followed by the fraction in binary), then the mean
171  * balance is 3.906. (Flajolet and Saheb analyzed the algorithm based on
172  * the original von Neumann method and showed that the balance is 5.680 in
173  * that case.)
174  *
175  * For \e bits large, the mean number of random digits is exp(1/2)/(1
176  * &minus; exp(&minus;1/2)) = 4.19 (versus 4.30 digits for the original
177  * method).
178  *
179  * @tparam Random the type of the random generator.
180  * @param[in,out] r a random generator.
181  * @return the random sample.
182  **********************************************************************/
183  template<class Random> RandomNumber<bits> operator()(Random& r) const;
184  private:
185  /**
186  * Return true with probability exp(&minus;\e p) for \e p in (0,1/2).
187  **********************************************************************/
188  template<class Random> bool
189  ExpFraction(Random& r, RandomNumber<bits>& p) const;
190  mutable RandomNumber<bits> _x;
191  mutable RandomNumber<bits> _v;
192  mutable RandomNumber<bits> _w;
193  };
194 
195  template<int bits> template<class Random> RandomNumber<bits>
197  // A simple rejection method gives the 1/2 fractional part. The number of
198  // rejections gives the multiples of 1/2.
199  //
200  // bits: used fract un-bin balance double
201  // original stats: 9.31615 2.05429 3.63628 5.67987 61.59456
202  // new stats: 7.23226 1.74305 3.32500 3.90725 59.82198
203  //
204  // The difference between un-bin and fract is exp(1)/(exp(1)-1) = 1.58198
205  _x.Init();
206  int k = 0;
207  while (!ExpFraction(r, _x)) { // Executed 1/(1 - exp(-1/2)) on average
208  ++k;
209  _x.Init();
210  }
211  if (k & 1) _x.RawDigit(0) += 1U << (bits - 1);
212  _x.AddInteger(k >> 1);
213  return _x;
214  }
215 
216  template<int bits> template<class Random> bool
218  const {
219  // The early bale out
220  if (p.Digit(r, 0) >> (bits - 1)) return false;
221  // Implement the von Neumann algorithm
222  _w.Init();
223  if (!_w.LessThan(r, p)) // if (w < p)
224  return true;
225  while (true) { // Unroll loop to avoid copying RandomNumber
226  _v.Init(); // v = r.Fixed();
227  if (!_v.LessThan(r, _w)) // if (v < w)
228  return false;
229  _w.Init(); // w = r.Fixed();
230  if (!_w.LessThan(r, _v)) // if (w < v)
231  return true;
232  }
233  }
234 
235 } // namespace RandomLib
236 
237 #if defined(_MSC_VER)
238 # pragma warning (pop)
239 #endif
240 
241 #endif // RANDOMLIB_EXACTEXPONENTIAL_HPP
Header for RandomNumber.
Generate random integers, reals, and booleans.
Infinite precision random numbers.
RandomNumber< bits > operator()(Random &r) const
unsigned Digit(Random &r, unsigned k)
Sample exactly from an exponential distribution.