What is performance-wise the best way to generate random bools?

前端 未结 9 2173
滥情空心
滥情空心 2020-12-23 21:14

I need to generate random Boolean values on a performance-critical path.

The code which I wrote for this is

std::random_device   rd;
std::uniform_int         


        
相关标签:
9条回答
  • 2020-12-23 21:54

    Some quick benchmarks (code):

       647921509 RandomizerXorshiftPlus
       821202158 BoolGenerator2 (reusing the same buffer)
      1065582517 modified Randomizer
      1130958451 BoolGenerator2 (creating a new buffer as needed)
      1140139042 xorshift128plus
      2738780431 xorshift1024star
      4629217068 std::mt19937
      6613608092 rand()
      8606805191 std::bernoulli_distribution
     11454538279 BoolGenerator
     19288820587 std::uniform_int_distribution
    

    For those who want ready-to-use code, I present XorShift128PlusBitShifterPseudoRandomBooleanGenerator, a tweaked version of RandomizerXorshiftPlus from the above link. On my machine, it is about as fast as @SergeRogatch's solution, but consistently about 10-20% faster when the loop count is high (≳100,000), and up to ~30% slower with smaller loop counts.

    class XorShift128PlusBitShifterPseudoRandomBooleanGenerator {
    public:
      bool randBool() {
        if (counter == 0) {
          counter = sizeof(GeneratorType::result_type) * CHAR_BIT;
          random_integer = generator();
        }
        return (random_integer >> --counter) & 1;
      }
    
    private:
      class XorShift128Plus {
      public:
        using result_type = uint64_t;
    
        XorShift128Plus() {
          std::random_device rd;
          state[0] = rd();
          state[1] = rd();
        }
    
        result_type operator()() {
          auto x = state[0];
          auto y = state[1];
          state[0] = y;
          x ^= x << 23;
          state[1] = x ^ y ^ (x >> 17) ^ (y >> 26);
          return state[1] + y;
        }
    
      private:
        result_type state[2];
      };
    
      using GeneratorType = XorShift128Plus;
    
      GeneratorType generator;
      GeneratorType::result_type random_integer;
      int counter = 0;
    };
    
    0 讨论(0)
  • 2020-12-23 21:55

    Unless you have further constraints on the randomness you need, the fastest way to generate a random bool is:

    bool RandomBool() { return false; }
    

    To be more specific, there are thousands of ways to generate random boolean numbers, all satisfying different constraints, and many of them do not deliver "truly" random numbers (that includes all the other answers so far). The word "random" alone does not tell anyone what properties you really need.

    0 讨论(0)
  • 2020-12-23 21:59

    iI think that best way is an using of precalculated random array:

    uint8_t g_rand[UINT16_MAX];
    bool InitRand()
    {
        for (size_t i = 0, n = UINT16_MAX; i < n; ++i)
            g_rand[i] = ::rand() & 1;
        return true;
    }
    bool g_inited = InitRand();
    inline const uint8_t * Rand()
    {
        return g_rand + (::rand()&INT16_MAX);
    }
    

    It using to fill some array dst[size]:

    const size_t size = 10000;
    bool dst[size];
    for (size_t i = 0; i < size; i += INT16_MAX)
         memcpy(dst + i, Rand(), std::min<size_t>(INT16_MAX, size - col));
    

    Of course you can initialize pre-calculated array with using of another random function.

    0 讨论(0)
  • 2020-12-23 22:00

    I would prefill a (long enough) (circular) buffer of 64bit random values, and then take very quickly one bit at a time when in need of a boolean random value

    #include <stdint.h>
    
    class BoolGenerator {
      private:
      const int BUFFER_SIZE = 65536;
      uint64_t randomBuffer[BUFFER_SIZE];
      uint64_t mask;
      int counter;
    
      void advanceCounter {
        counter++;
        if (counter == BUFFER_SIZE) {
            counter = 0;
        }
      }
    
      public:
      BoolGenerator() {
        //HERE FILL YOUR BUFFER WITH A RANDOM GENERATOR
        mask = 1;
        counter = 0;
      }
    
      bool generate() {
        mask <<= 1;
        if (!mask) { //After 64 shifts the mask becomes zero
            mask = 1;//reset mask
            advanceCounter();//get the next value in the buffer
        }
        return randomBuffer[counter] & mask;
      }
    }
    

    Of course the class can be made general to the buffer size, the random generator, the base type (doesn't necessarily have to be uint64_t) etc.


    Accessing the buffer only once every 64 calls:

    #include <stdint.h> //...and much more
    
    class BoolGenerator {
      private:
      static const int BUFFER_SIZE = 65536;
      uint64_t randomBuffer[BUFFER_SIZE];
      uint64_t currValue;
      int bufferCounter;
      int bitCounter;
    
      void advanceBufferCounter() {
        bufferCounter++;
        if (bufferCounter == BUFFER_SIZE) {
            bufferCounter = 0;
        }
      }
    
      void getNextValue() {
          currValue = randomBuffer[bufferCounter];
          bitCounter = sizeof(uint64_t) * 8;
          advanceBufferCounter();
      }
    
      //HERE FILL YOUR BUFFER WITH A RANDOM GENERATOR
      void initializeBuffer() {
      //Anything will do, taken from here: http://stackoverflow.com/a/19728404/2436175
          std::random_device rd;
          std::mt19937 rng(rd());
          std::uniform_int_distribution<uint64_t> uni(0,std::numeric_limits<uint64_t>::max());
          for (int i = 0; i < BUFFER_SIZE; i++ ) {
              randomBuffer[i] = uni(rng);
          }
      }
    
      public:
      BoolGenerator() {
          initializeBuffer();
          bufferCounter = 0;
          getNextValue();
      }
    
      bool generate() {
          if (!bitCounter) {
               getNextValue();
          }
          //A variation of other methods seen around
          bitCounter--;
          bool retVal = currValue & 0x01;
          currValue >>= 1;
          return retVal;
      }
    };
    
    0 讨论(0)
  • 2020-12-23 22:01

    For the purpose of performance, at a price of less "randomness" than e.g. std::mt19937_64, you can use Xorshift+ to generate 64-bit numbers and then use the bits of those numbers as pseudo-random booleans.

    Quoting the Wikipedia:

    This generator is one of the fastest generators passing BigCrush

    Details: http://xorshift.di.unimi.it/ . There is a comparison table in the middle of the page, showing that mt19937_64 is 2 times slower and is systematic.

    Below is sample code (the real code should wrap it in a class):

    #include <cstdint>
    #include <random>
    using namespace std;
    
    random_device rd;
    /* The state must be seeded so that it is not everywhere zero. */
    uint64_t s[2] = { (uint64_t(rd()) << 32) ^ (rd()),
        (uint64_t(rd()) << 32) ^ (rd()) };
    uint64_t curRand;
    uint8_t bit = 63;
    
    uint64_t xorshift128plus(void) {
        uint64_t x = s[0];
        uint64_t const y = s[1];
        s[0] = y;
        x ^= x << 23; // a
        s[1] = x ^ y ^ (x >> 17) ^ (y >> 26); // b, c
        return s[1] + y;
    }
    
    bool randBool()
    {
        if(bit >= 63)
        {
            curRand = xorshift128plus();
            bit = 0;
            return curRand & 1;
        }
        else
        {
            bit++;
            return curRand & (1<<bit);
        }
    }
    
    0 讨论(0)
  • 2020-12-23 22:01

    if performance is important, perhaps it's a good idea to generate a 32 bit random number and use each separate bit of it, something like this:

    bool getRandBool() {
        static uint32_t randomnumber;
        static int i=0;
        if (i==0) {
            randomnumber = <whatever your favorite randonnumbergenerator is>;
            i=32;
        }
        return (randomnumber & 1<<--i); 
     }
    

    this way the generation only impacts every 32th call

    0 讨论(0)
提交回复
热议问题