/** * \file ExactNormal.hpp * \brief Header for ExactNormal * * Sample exactly from a normal distribution. * * Copyright (c) Charles Karney (2011-2012) and licensed * under the MIT/X11 License. For more information, see * http://randomlib.sourceforge.net/ **********************************************************************/ #if !defined(RANDOMLIB_EXACTNORMAL_HPP) #define RANDOMLIB_EXACTNORMAL_HPP 1 #include #include // for max/min #if defined(_MSC_VER) // Squelch warnings about constant conditional expressions # pragma warning (push) # pragma warning (disable: 4127) #endif namespace RandomLib { /** * \brief Sample exactly from a normal distribution. * * Sample \e x from exp(−x2/2) / sqrt(2π). For * background, see: * - J. von Neumann, Various Techniques used in Connection with Random * Digits, J. Res. Nat. Bur. Stand., Appl. Math. Ser. 12, 36--38 * (1951), reprinted in Collected Works, Vol. 5, 768--770 (Pergammon, * 1963). * - D. E. Knuth and A. C. Yao, The Complexity of Nonuniform Random Number * Generation, in "Algorithms and Complexity" (Academic Press, 1976), * pp. 357--428. * - P. Flajolet and N. Saheb, The Complexity of Generating an Exponentially * Distributed Variate, J. Algorithms 7, 463--488 (1986). * * The algorithm is given in * - C. F. F. Karney, Sampling exactly from the normal distribution, * http://arxiv.org/abs/1303.6257 (Mar. 2013). * . * In brief, the algorithm is: * -# Select an integer \e k ≥ 0 with probability * exp(−k/2) (1−exp(−1/2)). * -# Accept with probability * exp(− \e k (\e k − 1) / 2); otherwise, reject and start * over at step 1. * -# Sample a random number \e x uniformly from [0,1). * -# Accept with probability exp(− \e x (\e x + 2\e k) / 2); * otherwise, reject and start over at step 1. * -# Set \e x = \e k + \e x. * -# With probability 1/2, negate \e x. * -# Return \e x. * . * It is easy to show that this algorithm returns samples from the normal * distribution with zero mean and unit variance. Futhermore, all these * steps can be carried out exactly as follows: * - Step 1: * - \e k = 0; * - while (ExpProb(−1/2)) increment \e k by 1. * - Step 2: * - \e n = \e k (\e k − 1) / 2; * - while (\e n > 0) * { if (!ExpProb(−1/2)) go to step 1; decrement \e n by 1; } * - Step 4: * - repeat \e k + 1 times: * if (!ExpProb(− \e x (\e x + 2\e k) / (2\e k + 2))) go to step 1. * . * Here, ExpProb(−\e p) returns true with probability exp(−\e p). * With \e p = 1/2 (steps 1 and 2), this is implemented with von Neumann's * rejection technique: * - Generate a sequence of random numbers Ui and * find the greatest \e n such that 1/2 > U1 > * U2 > . . . > Un. (The * resulting value of \e n may be 0.) * - If \e n is even, accept and return true; otherwise (\e n odd), reject * and return false. * . * For \e p = \e x (\e x + 2\e k) / (2\e k + 2) (step 4), we generalize von * Neumann's procedure as follows: * - Generate two sequences of random numbers Ui * and Vi and find the greatest \e n such that * both the following conditions hold * - \e x > U1 > U2 > . . . > * Un; * - Vi < (\e x + 2 \e k) / (2 \e k + 2) for * all \e i in [1, \e n]. * . * (The resulting value of \e n may be 0.) * - If \e n is even, accept (return true); otherwise (\e n odd), reject * (return false). * . * Here, instead of testing Vi < (\e x + 2 \e k) * / (2 \e k + 2), we carry out the following tests: * - return true, with probability 2 \e k / (2 \e k + 2); * - return false, with probability 1 / (2 \e k + 2); * - otherwise (also with probability 1 / (2 \e k + 2)), * return \e x > Vi. * . * The resulting method now entails evaluation of simple fractional * probabilities (e.g., 1 / (2 \e k + 2)), or comparing random numbers (e.g., * U1 > U2). These may be carried out * exactly with a finite mean running time. * * With \e bits = 1, this consumes 30.1 digits on average and the result has * 1.19 digits in the fraction. It takes about 676 ns to generate a result * (1460 ns, including the time to round it to a double). With bits = 32, it * takes 437 ns to generate a result (621 ns, including the time to round it * to a double). In contrast, NormalDistribution takes about 44 ns to * generate a double result. * * Another way of assessing the efficiency of the algorithm is thru the mean * value of the balance = (number of random bits consumed) − (number of * bits in the result). If we code the result in Knuth & Yao's unary-binary * notation, then the mean balance is 26.6. * * For example the following samples from a normal exponential distribution * and prints various representations of the result. * \code #include #include RandomLib::Random r; const int bits = 1; RandomLib::ExactNormal ndist; for (size_t i = 0; i < 10; ++i) { RandomLib::RandomNumber x = ndist(r); // Sample std::pair z = x.Range(); std::cout << x << " = " // Print in binary with ellipsis << "(" << z.first << "," << z.second << ")"; // Print range double v = x.Value(r); // Round exactly to nearest double std::cout << " = " << v << "\n"; } \endcode * Here's a possible result: \verbatim -1.00... = (-1.25,-1) = -1.02142 -0.... = (-1,0) = -0.319708 0.... = (0,1) = 0.618735 -0.0... = (-0.5,0) = -0.396591 0.0... = (0,0.5) = 0.20362 0.0... = (0,0.5) = 0.375662 -1.111... = (-2,-1.875) = -1.88295 -1.10... = (-1.75,-1.5) = -1.68088 -0.... = (-1,0) = -0.577547 -0.... = (-1,0) = -0.890553 \endverbatim * First number is in binary with ... indicating an infinite sequence of * random bits. Second number gives the corresponding interval. Third * number is the result of filling in the missing bits and rounding exactly * to the nearest representable double. * * This class uses some mutable RandomNumber objects. So a single * ExactNormal object cannot safely be used by multiple threads. In a * multi-processing environment, each thread should use a thread-specific * ExactNormal object. In addition, these should be invoked with * thread-specific random generator objects. * * @tparam bits the number of bits in each digit. **********************************************************************/ template class ExactNormal { public: /** * Return a random deviate with a normal distribution of mean 0 and * variance 1. * * @tparam Random the type of the random generator. * @param[in,out] r a random generator. * @return the random sample. **********************************************************************/ template RandomNumber operator()(Random& r) const; private: /** * Return true with probability exp(−1/2). For \e bits = 1, this * consumes, on average, \e t = 2.846 random digits. We have \e t = \e a * (1−exp(−1/2)) + \e b exp(−1/2), where \e a is the mean * bit count for false result = 3.786 and \e b is the mean bit count for * true result = 2.236. **********************************************************************/ template bool ExpProbH(Random& r) const; /** * Return true with probability exp(−n/2). For \e bits = 1, * this consumes, on average, \e t * (1−exp(−n/2))/(1−exp(−1/2)) random * digits. A true result uses \e n \e b random digits. A false result * uses \e a + \e b [exp(−1/2)/(1−exp(−1/2)) − * n exp(−n/2)/(1−exp(−n/2))] random * digits. **********************************************************************/ template bool ExpProb(Random& r, unsigned n) const; /** * Return \e n with probability exp(−n/2) * (1−exp(−1/2)). For \e bits = 1, this consumes \e n \e a + * \e b random digits if the result is \e n. Averaging over \e n this * becomes (\e b − (\e b − \e a) exp(−1/2))/(1 − * exp(−1/2)) digits. **********************************************************************/ template unsigned ExpProbN(Random& r) const; /** * Return true with probability 1/2. This is similar to r.Boolean() but * forces all the random results to come thru RandomNumber::RandomDigit. **********************************************************************/ template static bool Boolean(Random& r) { // A more general implementation which deals with the case where the base // might be negative is: // // const unsigned base = 1u << bits; // unsigned b; // do // b = RandomNumber::RandomDigit(r); // while (b == (base / 2) * 2); // return b & 1u; return RandomNumber::RandomDigit(r) & 1u; } /** * Implement outcomes for choosing with prob (\e x + 2\e k) / (2\e k + 2); * return: * - 1 (succeed unconditionally) with prob (2\e k) / (2\e k + 2), * - 0 (succeed with probability x) with prob 1 / (2\e k + 2), * - −1 (fail unconditionally) with prob 1 / (2\e k + 2). * . * This simulates \code double x = r.Fixed(); // Uniform in [0,1) x *= (2 * k + 2); return x < 2 * k ? 1 : (x < 2 * k + 1 ? 0 : -1); \endcode **********************************************************************/ template static int Choose(Random& r, int k) { // Limit base to 2^15 to avoid integer overflow const int b = bits > 15 ? 15 : bits; const unsigned mask = (1u << b) - 1; const int m = 2 * k + 2; int n1 = m - 2, n2 = m - 1; // Evaluate u < n/m where u is a random real number in [0,1). Write u = // (d + u') / 2^b where d is a random integer in [0,2^b) and u' is in // [0,1). Then u < n/m becomes u' < n'/m where n' = 2^b * n - d * m and // exit if n' <= 0 (false) or n' >= m (true). while (true) { int d = (mask & RandomNumber::RandomDigit(r)) * m; n1 = (std::max)((n1 << b) - d, 0); if (n1 >= m) return 1; n2 = (std::min)((n2 << b) - d, m); if (n2 <= 0) return -1; if (n1 == 0 && n2 == m) return 0; } } mutable RandomNumber _x; mutable RandomNumber _p; mutable RandomNumber _q; }; template template bool ExactNormal::ExpProbH(Random& r) const { // Bit counts // ExpProbH: 2.846 = 3.786 * (1-exp(-1/2)) + 2.236 * exp(-1/2) // t = a * (1-exp(-1/2)) + b * exp(-1/2) // t = mean bit count for result = 2.846 // a = mean bit count for false result = 3.786 // b = mean bit count for true result = 2.236 // // for bits large // t = exp(1/2) = 1.6487 // a = exp(1/2)/(2*(1-exp(-1/2))) = 2.0951 // b = exp(1/2)/(2*exp(-1/2)) = 1.3591 // // Results for Prob(exp(-1)), omitting first test // total = 5.889, false = 5.347, true = 6.826 // // Results for Prob(exp(-1)) using ExpProbH(r) && ExpProbH(r), // total = 4.572 = (1 - exp(-1)) * a + (1 + exp(-1/2)) * exp(-1/2) * b // false = 4.630 = a + b * exp(-1/2)/(1 + exp(-1/2)), // true = 4.472 = 2 * b _p.Init(); if (_p.Digit(r, 0) >> (bits - 1)) return true; while (true) { _q.Init(); if (!_q.LessThan(r, _p)) return false; _p.Init(); if (!_p.LessThan(r, _q)) return true; } } template template bool ExactNormal::ExpProb(Random& r, unsigned n) const { // Bit counts // ExpProb(n): t * (1-exp(-n/2))/(1-exp(-1/2)) // ExpProb(n) = true: n * b // ExpProb(n) = false: a + // b * (exp(-1/2)/(1-exp(-1/2)) - n*exp(-n/2)/(1-exp(-n/2))) while (n--) { if (!ExpProbH(r)) return false; } return true; } template template unsigned ExactNormal::ExpProbN(Random& r) const { // Bit counts // ExpProbN() = n: n * a + b unsigned n = 0; while (ExpProbH(r)) ++n; return n; } template template RandomNumber ExactNormal::operator()(Random& r) const { // With bits = 1, // - mean number of bits used = 30.10434 // - mean number of bits in fraction = 1.18700 // - mean number of bits in result = 3.55257 (unary-binary) // - mean balance = 30.10434 - 3.55257 = 26.55177 // - mean number of bits to generate a double = 83.33398 // . // Note // - unary-binary notation (Knuth + Yao, 1976): write x = n + y, with n = // integer and y in [0,1). If n >=0, then write (n+1) 1's followed by a // 0; otherwise (n < 0), write (-n) 0's followed by a 1. Write y as a // binary fraction. // - (bits in result) - (bits in fraction) = 2 (for encoding overhead for // the integer part) + 0.36557, where 0.36557 = (bits used for integer // part) = sum(k*int(sqrt(2/pi)*exp(-x^2/2), x=k..k+1), k=0..inf) // - (bits for double) approx (bits used) - (bits in fraction) + 1 (for // guard bit) + 53.41664 where 53.41664 = (bits in fraction of double) = // sum((52-l)*int(sqrt(2/pi)*exp(-x^2/2), x=2^l,2^(l+1)), l=-inf..inf) // This is approximate because it doesn't account for the minimum // exponent, denormalized numbers, and rounding changing the exponent. // while (true) { // Executed sqrt(2/pi)/(1-exp(-1/2)) = 2.027818889827955 times on // average. unsigned k = ExpProbN(r); // the integer part of the result. if (ExpProb(r, (k - 1) * k)) { // Probability that this test succeeds is // (1 - exp(-1/2)) * sum(exp(-k/2) * exp(-(k-1)*k/2), k=0..inf)) // = (1 - exp(-1/2)) * G = 0.689875359564630 // where G = sum(exp(-k^2/2, k=0..inf) = 1.75331414402145 // For k == 0, sample from exp(-x^2/2) for x in [0,1]. This succeeds // with probability int(exp(-x^2/2),x=0..1). // // For general k, substitute x' = x + k in exp(-x'^2/2), and obtain // exp(-k^2/2) * exp(-x*(x+2*k)/2). So sample from exp(-x*(x+2*k)/2). // This succeeds with probability int(exp(-x*(x+2*k)/2),x=0..1) = // int(exp(-x^2/2),x=k..k+1)*exp(k^2/2) = // // 0.8556243918921 for k = 0 // 0.5616593588061 for k = 1 // 0.3963669350376 for k = 2 // 0.2974440159655 for k = 3 // 0.2345104783458 for k = 4 // 0.1921445042826 for k = 5 // // Returns a result with prob sqrt(pi/2) / G = 0.714825772431666; // otherwise another trip through the outer loop is taken. _x.Init(); unsigned s = 1; for (unsigned j = 0; j <= k; ++j) { // execute k + 1 times bool first; for (s = 1, first = true; ; s ^= 1, first = false) { // A simpler algorithm is indicated by ALT, results in // - mean number of bits used = 29.99968 // - mean number of bits in fraction = 1.55580 // - mean number of bits in result = 3.92137 (unary-binary) // - mean balance = 29.99968 - 3.92137 = 26.07831 // - mean number of bits to generate a double = 82.86049 // . // This has a smaller balance (by 0.47 bits). However the number // of bits in the fraction is larger by 0.37 if (first) { // ALT: if (false) { // This implements the success prob (x + 2*k) / (2*k + 2). int y = Choose(r, k); if (y < 0) break; // the y test fails _q.Init(); if (y > 0) { // the y test succeeds just test q < x if (!_q.LessThan(r, _x)) break; } else { // the y test is ambiguous // Test max(q, p) < x. List _q before _p since it ends up with // slightly more digits generated (and these will be used // subsequently). (_p's digits are immediately thrown away.) _p.Init(); if (!_x.GreaterPair(r, _q, _p)) break; } } else { // Split off the failure test for k == 0, i.e., factor the prob // x/2 test into the product: 1/2 (here) times x (in assignment // of y). if (k == 0 && Boolean(r)) break; // ALT: _q.Init(); if (!_q.LessThan(r, first ? _x : _p)) break; _q.Init(); if (!_q.LessThan(r, _p)) break; // succeed with prob k == 0 ? x : (x + 2*k) / (2*k + 2) int y = k == 0 ? 0 : Choose(r, k); if (y < 0) break; else if (y == 0) { _p.Init(); if (!_p.LessThan(r, _x)) break; } } _p.swap(_q); // a fast way of doing p = q } if (s == 0) break; } if (s != 0) { _x.AddInteger(k); if (Boolean(r)) _x.Negate(); // half of the numbers are negative return _x; } } } } } // namespace RandomLib #if defined(_MSC_VER) # pragma warning (pop) #endif #endif // RANDOMLIB_EXACTNORMAL_HPP