性能方面是什么是生成随机bool的最佳方法?

时间:2016-02-12 09:02:15

标签: c++ c++11 random boolean c++14

我需要在性能关键路径上生成随机布尔值。

我为此写的代码是

std::random_device   rd;
std::uniform_int_distribution<> randomizer(0, 1);
const int val randomizer(std::mt19937(rd()));
const bool isDirectionChanged = static_cast<bool>(val);

但是不要认为这是最好的方法,因为我不喜欢static_cast<bool>

在网络上我找到了更多解决方案

1. std::bernoulli_distribution

2. bool randbool = rand() & 1;请务必在开头致电srand()

9 个答案:

答案 0 :(得分:39)

出于演出目的,价格低于“随机性”的价格。 std::mt19937_64,您可以使用Xorshift+生成64位数字,然后将这些数字的位用作伪随机布尔值。

引用维基百科:

  

这个生成器是传递BigCrush的最快的生成器之一

详细信息:http://xorshift.di.unimi.it/。页面中间有一个比较表,显示mt19937_64慢2倍且系统化。

下面是示例代码(真正的代码应该将它包装在一个类中):

#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);
    }
}

答案 1 :(得分:18)

一些快速基准(code):

XorShift128PlusBitShifterPseudoRandomBooleanGenerator

对于那些想要使用现成代码的人,我会从上面的链接中提出RandomizerXorshiftPlusclass 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; }; 的调整版本。在我的机器上,它与@ SergeRogatch的解决方案一样快,但在循环计数高(≳100,000)时一直快10-20%,在循环次数较少的情况下慢约30%。 / p>

class Float
  def round_half_to_even(precision)
    if self % 1 == 0.5
      floor = self.to_i.to_f
      return floor % 2 == 0 ? floor : floor + 1
    else
      return self.round(precision)
    end
  end
end

答案 2 :(得分:10)

一种方法是仅为评论中所述的每64个随机调用生成unsigned long long。一个例子:

#include <random>
class Randomizer
{
public:
    Randomizer() : m_rand(0), counter(0), randomizer(0, std::numeric_limits<unsigned long long>::max()) {}

    bool RandomBool()
    {
        if (!counter)
        {
            m_rand = randomizer(std::mt19937(rd()));
            counter = sizeof(unsigned long long) * 8;

        }
        return (m_rand >> --counter) & 1;
    }
private:
    std::random_device  rd;
    std::uniform_int_distribution<unsigned long long> randomizer;
    unsigned long long m_rand;
    int counter;
};

答案 3 :(得分:4)

我会预先填充64位随机值的(足够长的)(循环)缓冲区,然后在需要布尔随机值时一次非常快速地取一个位

#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;
  }
}

当然这个类可以通用于缓冲区大小,随机生成器,基类型(不一定是uint64_t)等。

每64次调用仅访问一次缓冲区:

#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;
  }
};

答案 4 :(得分:3)

除非你对所需的随机性有进一步的限制,否则生成随机bool的最快方法是:

bool RandomBool() { return false; }

更具体地说,有数千种方法可以生成随机布尔值,所有这些方法都满足不同的约束条件,而且其中许多方法都不能提供“真正的”随机数(到目前为止包含所有其他答案)。单词“随机”并不能告诉任何人你真正需要什么属性。

答案 5 :(得分:1)

如果效果是您唯一的标准,那么answer就是:

bool get_random()
{
    return true; // chosen by fair coin flip.
                 // guaranteed to be random.
}

不幸的是,这个随机数的熵是零,但性能非常快。

由于我怀疑这个随机数生成器对你不是很有用,你需要量化你希望你的布尔值是多么随机。 2048的周期长度怎么样?一百万? 2 ^ 19937-1?直到宇宙结束?

我怀疑,既然你明确表示性能是你最关心的问题,那么一个好的老式线性同余生成器可能“足够好”。基于this article,我猜这个生成器的周期大约是32 *((2 ^ 31)-5),或大约68万亿次迭代。如果那不是“足够好”,你可以放入你喜欢的任何C ++ 11兼容的生成器而不是minstd_rand。

要获得额外的功劳,并且效果不佳,请修改以下代码,以使用biased coin algorithm消除生成器中的偏差。

#include <iostream>
#include <random>

bool get_random()
{
    typedef std::minstd_rand generator_type;
    typedef generator_type::result_type result_type;

    static generator_type generator;
    static unsigned int bits_remaining = 0;
    static result_type random_bits;

    if ( bits_remaining == 0 )
    {
        random_bits = generator();
        bits_remaining = sizeof( result_type ) * CHAR_BIT - 1;
    }

    return ( ( random_bits & ( 1 << bits_remaining-- ) ) != 0 );
}

int main()
{
    for ( unsigned int i = 0; i < 1000; i++ )
    {
        std::cout << " Choice " << i << ": ";
        if ( get_random() )
            std::cout << "true";
        else
            std::cout << "false";

        std::cout << std::endl;
    }
}

答案 6 :(得分:0)

我认为最好的方法是使用预先计算的随机数组:

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);
}

用于填充某些数组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));

当然,您可以使用另一个随机函数初始化预先计算的数组。

答案 7 :(得分:0)

如果性能很重要,或许最好生成一个32位随机数并使用它的每个单独位,如下所示:

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); 
 }

这种方式只会影响每第32次呼叫

答案 8 :(得分:0)

显然我必须添加另一个答案。刚刚发现从Ivy Bridge架构开始,英特尔增加了RdRand CPU指令,AMD在2015年6月晚些时候添加了它。所以如果你的目标是处理器足够新并且不介意使用(内联)汇编,生成随机bool的最快方法应该是调用RdRand CPU指令来获取一个64位随机数,如here所述(滚动到大约中间位置)代码示例页面(在该链接上还有一个代码示例,用于检查当前CPU是否支持RdRand指令,另请参阅Wikipedia以获取有关如何使用CPUID指令执行此操作的说明),然后使用我的Xorshit+ based answer中描述的布尔数字。