web-dev-qa-db-ja.com

ランダムブールを生成するためのパフォーマンス面での最良の方法は何ですか?

パフォーマンスが重要なパスでランダムなブール値を生成する必要があります。

このために書いたコードは

_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()を呼び出すことを忘れないでください。

42
T M

パフォーマンスのために、たとえば「ランダム性」よりも低い価格で。 std::mt19937_64Xorshift + を使用して64ビットの数値を生成し、それらの数値のビットを擬似乱数ブールとして使用できます。

ウィキペディアを引用:

このジェネレーターは、BigCrushを渡す最速のジェネレーターの1つです。

詳細: 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);
    }
}
39
Serge Rogatch

いくつかの簡単なベンチマーク( 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

すぐに使用できるコードが必要な場合は、XorShift128PlusBitShifterPseudoRandomBooleanGenerator、上記のリンクからのRandomizerXorshiftPlusの微調整バージョン。私のマシンでは、@ SergeRogatchのソリューションとほぼ同じ速度ですが、ループカウントが高い(≳100,000)場合は常に約10〜20%速く、ループカウントが小さい場合は最大約30%遅くなります。

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;
};
18
emlai

方法は、コメントに記載されているように、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;
};
10

64ビットのランダム値の(十分な長さの)(循環)バッファを事前に入力し、ブールのランダム値が必要なときに一度に1ビットずつ非常に迅速に取得します

#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回の呼び出しごとに1回だけバッファーにアクセスします。

#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
Antonio

必要なランダム性にさらに制約がない限り、ランダムboolを生成する最も速い方法は次のとおりです。

bool RandomBool() { return false; }

具体的には、ランダムなブール値を生成する数千の方法があり、すべてがさまざまな制約を満たしますが、その多くは「これまでのすべての答えを含む」「真の」乱数を提供しません。 「ランダム」という言葉だけでは、本当に必要なプロパティは誰にもわかりません。

3
Peter

パフォーマンスが唯一の基準である場合、 answer は次のとおりです。

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

残念ながら、この乱数のエントロピーはゼロですが、パフォーマンスは非常に高速です。

この乱数ジェネレーターはあまり役に立たないと思われるので、ブール値をどの程度ランダムにするかを定量化する必要があります。 2048のサイクル長はどうですか? 100万? 2 ^ 19937-1?宇宙の終わりまで?

パフォーマンスが最大の関心事であると明示的に述べているので、古き良き線形合同ジェネレータは「十分」であると思われます。 この記事 に基づいて、このジェネレーターの周期は約32 *((2 ^ 31)-5)、つまり約68兆回の反復であると推測しています。それが「十分」でない場合は、minstd_Randの代わりに任意のC++ 11互換ジェネレーターをドロップできます。

余分なクレジットと小さなパフォーマンスヒットについては、以下のコードを変更して 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;
    }
}
1
johnwbyrd

最善の方法は、事前に計算されたランダム配列を使用することだと思います。

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

もちろん、別のランダム関数を使用して、事前に計算された配列を初期化できます。

0
ErmIg

どうやら私は別の答えを追加する必要があります。 Ivy Bridge アーキテクチャで始まるIntelが RdRand CPU命令を追加し、AMDが2015年6月にそれを追加したことを理解しただけです。 (インライン)アセンブリを使用しても構いません。ランダムなboolsを生成する最速の方法は、RdRand CPU命令を呼び出して64ビットの乱数を取得することです here =(コード例についてはページのほぼ中央までスクロールします)(そのリンクには、現在のCPUでRdRand命令のサポートを確認するコード例もあります。また、CPUIDでこれを行う方法については、Wikipediaを参照してくださいそして、私の Xorshit +ベースの回答 で説明されているように、ブール値にその数のビットを使用します。

0
Serge Rogatch

パフォーマンスが重要な場合は、おそらく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番目の呼び出しごとにのみ影響します

0
Tommylee2k