ExRandom  3.0
unit_normal_kahn.hpp
Go to the documentation of this file.
1 /**
2  * @file unit_normal_kahn.hpp
3  * @author Charles Karney <charles.karney@sri.com>
4  * @brief Definition of unit_normal_kahn
5  *
6  * Copyright (c) Charles Karney (2014) and licensed under the MIT/X11 License.
7  * For more information, see http://exrandom.sourceforge.net/
8  */
9 
10 #if !defined(EXRANDOM_UNIT_NORMAL_KAHN_HPP)
11 #define EXRANDOM_UNIT_NORMAL_KAHN_HPP 1
12 
13 #include <utility> // for std::pair
14 
15 #include <exrandom/u_rand.hpp>
17 
18 namespace exrandom {
19 
20  /**
21  * @brief Sample u-rands from the normal distribution,
22  * Kahn's method (<b>deprecated</b>)
23  *
24  * This samples from the unit normal distribution
25  * P(x) = exp(&minus; x<sup>2</sup>/2) / &radic;(2&pi;) using Kahn's method
26  * This implements Algorithm K.
27  *
28  * @tparam digit_gen the type of digit generator.
29  *
30  * <b>WARNING</b>: One of the steps of this algorithm requires the use of
31  * arbitrary precision integer arithmetic. However in this implementation
32  * the arithmetic is carried out with 64-bit integers. As a result there is
33  * occasionally (1 time in 10<sup>9</sup>) overflow which results in a bias
34  * in the results. Use of this class is therefore <b>deprecated</b>.
35  *
36  * digit_gen::base cannot exceed 16.
37  */
38  template<typename digit_gen> class unit_normal_kahn {
39  public:
40  /**
41  * The constructor.
42  *
43  * @param D a reference to the digit generator to be used.
44  */
45  unit_normal_kahn(digit_gen& D) : _D(D), _x(D), _y(D), _e(D) {}
46 
47  /**
48  * Generate the next deviate as a u_rand.
49  *
50  * @tparam Generator the type of g.
51  * @param g the random generator engine.
52  * @param[out] x the u_rand to set.
53  */
54  template<typename Generator>
55  void generate(Generator& g, u_rand<digit_gen>& x) {
56  for (;;) {
57  // Generate a pair of exponential deviates
58  _e.generate(g, x); _e.generate(g, _y);
59  std::pair<long long, long long> px = x.rational();
60  std::pair<long long, long long> py = _y.rational();
61  for (;;) {
62  long long nxl = px.first, dx = px.second;
63  nxl -= dx; // x - 1
64  if (nxl < 0LL) nxl = -nxl-1; // abs(x - 1)
65  long long nxu = nxl + 1;
66  nxl *= nxl; nxu *= nxu; dx *= dx; // (x-1)^2
67  long long nyl = py.first, nyu = nyl + 1, dy = py.second;
68  // 2 * y
69  if (dy % 2 == 0)
70  dy /= 2;
71  else {
72  nyl *= 2; nyu *= 2;
73  }
74  if (!(dx > 0 && dy > 0))
75  // Overflow has happened! About 8 times out of 10^10 with b = 2.
76  break;
77  // Put on a common denominator max(dx, dy). Note that the lesser
78  // will always evenly divide the greater (even if dy is divided by 2
79  // above).
80  if (dy > dx) {
81  nxl *= dy/dx; nxu *= dy/dx;
82  } else if (dx > dy) {
83  nyl *= dx/dy; nyu *= dx/dy;
84  }
85  if (!(nxl >= 0 && nxu > nxl && nyl >= 0 && nyu > nyl))
86  // Overflow has happened! About 4 times out of 10^10 with b = 2.
87  break;
88  // compare (nxl,nxu) with (nyl,nyl). These are open ranges, so the
89  // tests below include equality.
90  if (nxu <= nyl) {
91  // (x - 1)^2 < 2*y, return x with random sign
92  if (_y.init().less_than_half(g)) x.negate();
93  return;
94  }
95  if (nyu <= nxl)
96  break; // (x - 1)^2 > 2*y, restart
97  // Narrow the widest gap. In case of a tie, refine x, since that
98  // contributes a extra digit in the result.
99  if (nxu - nxl >= nyu - nyl)
100  px = x.rational(g, x.ndigits() + 1);
101  else
102  py = _y.rational(g, _y.ndigits() + 1);
103  }
104  }
105  }
106 
107  /**
108  * Generate the next deviate and round it to a floating point number.
109  *
110  * @tparam RealType the floating point type of the result.
111  * @tparam Generator the type of g.
112  * @param g the random generator engine.
113  * @return the normal deviate.
114  */
115  template<typename RealType, typename Generator>
116  RealType value(Generator& g) {
117  generate(g, _x);
118  return _x.template value<RealType>(g);
119  }
120 
121  /**
122  * Return the midpoint of the next deviate with a specified number of
123  * digits.
124  *
125  * @tparam RealType the floating point type of the result.
126  * @tparam Generator the type of g.
127  * @param g the random generator engine.
128  * @param k the minimum number of digits in the fraction.
129  * @return the normal deviate.
130  *
131  * Note the @e k gives the numer of digits in the base @e base.
132  */
133  template<typename RealType, typename Generator>
134  RealType midpoint(Generator& g, size_t k) {
135  generate(g, _x);
136  return _x.template midpoint<RealType>(g, k);
137  }
138 
139  /**
140  * @return a reference to the digit generator used in the constructor.
141  */
142  digit_gen& digit_generator() const { return _D; }
143 
144  /**
145  * The base of the digit generator.
146  */
147  static const uint_t base = digit_gen::base;
148  private:
149  static const int bits = digit_gen::bits;
150  static const bool power_of_two = digit_gen::power_of_two;
151  // Allow base in [2,16]; large bases make overflow more likely.
152  static_assert(base && base <= 16, "base must not be greater than 16");
153  // Disable copy assignment
154  unit_normal_kahn& operator=(const unit_normal_kahn&);
155  digit_gen& _D;
156  u_rand<digit_gen> _x, _y; // temporary storage
158 
159  };
160 
161 }
162 
163 #endif // EXRANDOM_UNIT_NORMAL_KAHN_HPP
Sample u-rands from the normal distribution, Kahn's method (deprecated)
void generate(Generator &g, u_rand< digit_gen > &x)
std::pair< long long, long long > rational() const
Definition: u_rand.hpp:329
Definition of u_rand.
size_t ndigits() const
Definition: u_rand.hpp:159
RealType value(Generator &g)
RealType midpoint(Generator &g, size_t k)
digit_gen & digit_generator() const
The common namespace.
Definition: aux_info.hpp:18
Definition of unit_exponential_dist.
The machinery to handle u-rands, arbitrary precision random deviates.
Definition: u_rand.hpp:105
void generate(Generator &g, u_rand< digit_gen > &x)