LeadingZeros.hpp
Go to the documentation of this file.
00001 /**
00002  * \file LeadingZeros.hpp
00003  * \brief Header for LeadingZeros
00004  *
00005  * Count the leading zeros in a real number.
00006  *
00007  * Written by Charles Karney <charles@karney.com> and licensed under the LGPL.
00008  * For more information, see http://randomlib.sourceforge.net/
00009  **********************************************************************/
00010 
00011 #if !defined(LEADINGZEROS_HPP)
00012 #define LEADINGZEROS_HPP "$Id: LeadingZeros.hpp 6723 2010-01-11 14:20:10Z ckarney $"
00013 
00014 namespace RandomLib {
00015   /**
00016    * \brief Count of leading zeros.
00017    *
00018    * Count of leading zero bits after the binary point in a real number
00019    * uniformly distributed in (0,1).  (This is equivalent to the geometric
00020    * distribution with probability 1/2.)  For example
00021    * \code
00022    *   #include "RandomLib/LeadingZeros.hpp"
00023    *
00024    *   RandomLib::Random r;     // A RandomGenerator works here too
00025    *   std::cout << "Seed set to " << r.SeedString() << std::endl;
00026    *   LeadingZeros zeros;
00027    *   std::cout << "Count of leading zeros:";
00028    *   for (size_t i = 0; i < 20; ++i)
00029    *       std::cout << " " << zeros(r);
00030    *   std::cout << std::endl;
00031    * \endcode
00032    **********************************************************************/
00033   class LeadingZeros {
00034   public:
00035     /**
00036      * Return the number of zero bits after the binary point in a real number
00037      * uniformly distributed in (0,1).  Thus \e k is returned with probability
00038      * 1/2<sup><i>k</i>+1</sup>.  Because MT19937 is \e not a perfect random
00039      * number generator, this always returns a result in [0, 19937).
00040      **********************************************************************/
00041     template<class Random> unsigned operator()(Random& r) const throw();
00042   };
00043 
00044   template<class Random>
00045   inline unsigned LeadingZeros::operator()(Random& r) const throw() {
00046     // It's simpler to count the number of trailing ones in each w-bit block
00047     // stopping when we get to a zero bit.
00048     //
00049     // Process a word in chunks of size m.  The algorithm here can deal with
00050     // any m assuming that z is modified accordingly.  m = 4 is an approximate
00051     // optimum.
00052     //
00053     // Can also adapt this routine to use RandomNumber::highest_bit_idx
00054     // instead.  However the result is considerably slower.
00055     const int m = 4;
00056     // mask with m low bits set
00057     const typename Random::result_type mask = ~(Random::max << m);
00058     // Number of trailing 1 bits in [0, 1<<m).  However, correct results are
00059     // also obtained with any permutation of this array.  This particular
00060     // permutation is useful since the initial 1/2, 1/4, etc. can be used for
00061     // m-1, m-2, etc.  To generate the array for the next higher m, append a
00062     // duplicate of the array and increment the last entry by one.
00063     const unsigned z[1 << m] =
00064       { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 4, };
00065     typename Random::result_type x = r();
00066     for (unsigned b = m, n = 0; b < Random::width; b += m) {
00067       n += z[x & mask];         // count trailing 1s in chunk
00068       if (n < b)                // chunk contains a 0
00069         return n;
00070       x >>= m;                  // shift out the chunk we've processed
00071     }
00072     // x is all ones (prob 1/2^w); process the next word.
00073     return Random::width + operator()(r);
00074   }
00075 } // namespace RandomLib
00076 #endif  // LEADINGZEROS_HPP