/** * \file InversePiProb.hpp * \brief Header for InversePiProb * * Return true with probabililty 1/π. * * Copyright (c) Charles Karney (2012) and licensed * under the MIT/X11 License. For more information, see * http://randomlib.sourceforge.net/ **********************************************************************/ #if !defined(RANDOMLIB_INVERSEPIPROB_HPP) #define RANDOMLIB_INVERSEPIPROB_HPP 1 #include // for abs(int) #include namespace RandomLib { /** * \brief Return true with probability 1/π. * * InversePiProb p; p(Random& r) returns true with prob 1/π using the * method of Flajolet et al. It consumes 9.6365 bits per call on average. * * The method is given in Section 3.3 of * - P. Flajolet, M. Pelletier, and M. Soria,
* On Buffon Machines and Numbers,
Proc. 22nd ACM-SIAM Symposium on * Discrete Algorithms (SODA), Jan. 2011.
* http://www.siam.org/proceedings/soda/2011/SODA11_015_flajoletp.pdf
* . * using the identity * \f[ \frac 1\pi = \sum_{n=0}^\infty * {{2n}\choose n}^3 \frac{6n+1}{2^{8n+2}} \f] * * It is based on the expression for 1/π given by Eq. (28) of
* - S. Ramanujan,
* Modular Equations and Approximations to π,
* Quart. J. Pure App. Math. 45, 350--372 (1914);
* In Collected Papers, edited by G. H. Hardy, P. V. Seshu Aiyar, * B. M. Wilson (Cambridge Univ. Press, 1927; reprinted AMS, 2000).
* http://books.google.com/books?id=oSioAM4wORMC&pg=PA36
* . * \f[\frac4\pi = 1 + \frac74 \biggl(\frac 12 \biggr)^3 * + \frac{13}{4^2} \biggl(\frac {1\cdot3}{2\cdot4} \biggr)^3 * + \frac{19}{4^3} \biggl(\frac {1\cdot3\cdot5}{2\cdot4\cdot6} \biggr)^3 * + \ldots \f] * * The following is a description of how to carry out the algorithm "by hand" * with a real coin, together with a worked example: * -# Perform three coin tossing experiments in which you toss a coin until * you get tails, e.g., HHHHT; HHHT; HHT. Let * h1 = 4, h2 = 3, * h3 = 2 be the numbers of heads tossed in each * experiment. * -# Compute n = ⌊h1/2⌋ + * ⌊h2/2⌋ + * mod(⌊(h3 − 1)/3⌋, 2) = 2 + 1 + 0 * = 3. Here is a table of the 3 contributions to n:\verbatim 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 h 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 floor(h1/2) 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 floor(h2/2) 1 0 0 0 1 1 1 0 0 0 1 1 1 0 0 0 1 1 mod(floor((h3-1)/3), 2) \endverbatim * -# Perform three additional coin tossing experiments in each of which you * toss a coin 2n = 6 times, e.g., TTHHTH; * HHTHH|H; THHHHH. Are the number of heads and tails * equal in each experiment? yes and no and no → * false. (Here, you can give up at the |.) * . * The final result in this example is false. The most common way a * true result is obtained is with n = 0, in which case the * last step vacuously returns true. * * Proof of the algorithm: Flajolet et al. rearrange Ramanujan's identity as * \f[ \frac 1\pi = \sum_{n=0}^\infty * \biggl[{2n\choose n} \frac1{2^{2n}} \biggr]^3 * \frac{6n+1}{2^{2n+2}}. \f] * Noticing that * \f[ \sum_{n=0}^\infty * \frac{6n+1}{2^{2n+2}} = 1, \f] * the algorithm becomes: * -# pick n ≥ 0 with prob (6n+1) / 22n+2 * (mean n = 11/9); * -# return true with prob (binomial(2n, n) / * 22n)3. * * Implement (1) as * - geom4(r) + geom4(r) returns n with probability 9(n + * 1) / 22n+4; * - geom4(r) + geom4(r) + 1 returns n with probability * 36n / 22n+4; * - combine these with probabilities [4/9, 5/9] to yield (6n + * 1) / 22n+2, as required. * . * Implement (2) as the outcome of 3 coin tossing experiments of 2n * tosses with success defined as equal numbers of heads and tails in each * trial. * * This class illustrates how to return an exact result using coin tosses * only. A more efficient implementation (which is still exact) would * replace prob59 by r.Prob(5,9) and geom4 by LeadingZeros z; z(r)/2. **********************************************************************/ class InversePiProb { private: template bool prob59(Random& r) { // true with prob 5/9 = 0.1 000 111 000 111 000 111 ... (binary expansion) if (r.Boolean()) return true; for (bool res = false; ; res = !res) for (int i = 3; i--; ) if (r.Boolean()) return res; } template int geom4(Random& r) { // Geom(1/4) int sum = 0; while (r.Boolean() && r.Boolean()) ++sum; return sum; } template bool binom(Random& r, int n) { // Probability of equal heads and tails on 2*n tosses // = binomial(2*n, n) / 2^(2*n) int d = 0; for (int k = n; k--; ) d += r.Boolean() ? 1 : -1; for (int k = n; k--; ) { d += r.Boolean() ? 1 : -1; // This optimization saves 0.1686 bit per call to operator() on average. if (std::abs(d) > k) return false; } return true; } public: /** * Return true with probability 1/π. * * @tparam Random the type of the random generator. * @param[in,out] r a random generator. * @return true with probability 1/π. **********************************************************************/ template bool operator()(Random& r) { // Return true with prob 1/pi. int n = geom4(r) + geom4(r) + (prob59(r) ? 1 : 0); for (int j = 3; j--; ) if (!binom(r, n)) return false; return true; } }; } // namespace RandomLib #endif // RANDOMLIB_INVERSEPIPROB_HPP