42 votes

Quelle est la meilleure façon, du point de vue des performances, de générer des bools aléatoires ?

Je dois générer des valeurs booléennes aléatoires sur un chemin critique pour les performances.

Le code que j'ai écrit pour cela est

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

Mais ne pensez pas que c'est la meilleure façon de faire, car je n'aime pas le faire. static_cast<bool> .

Sur le web, j'ai trouvé quelques autres solutions

1. std::bernoulli_distribution

2. bool randbool = rand() & 1; N'oubliez pas d'appeler srand() au début.

39voto

Serge Rogatch Points 18

Pour des raisons de performance, au prix d'un moindre "hasard" que par exemple. std::mt19937_64 vous pouvez utiliser Xorshift+ pour générer des nombres de 64 bits, puis utiliser les bits de ces nombres comme des booléens pseudo-aléatoires.

Citant le Wikipedia :

Ce générateur est l'un des générateurs les plus rapides en passant par BigCrush.

Détails : http://xorshift.di.unimi.it/ . Il y a un tableau de comparaison au milieu de la page, qui montre que mt19937_64 est 2 fois plus lent et est systématique.

Voici un exemple de code (le vrai code doit être intégré dans une classe) :

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

19voto

zenith Points 18532

Quelques repères rapides ( 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

Pour ceux qui veulent du code prêt à l'emploi, je présente XorShift128PlusBitShifterPseudoRandomBooleanGenerator une version modifiée de RandomizerXorshiftPlus du lien ci-dessus. Sur ma machine, elle est à peu près aussi rapide que la solution de @SergeRogatch, mais constamment environ 10-20% plus rapide lorsque le nombre de boucles est élevé (≳100 000), et jusqu'à ~30% plus lente avec un nombre de boucles plus petit.

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

10voto

Gill Bates Points 3884

Une solution serait de générer simplement un unsigned long long pour chaque 64 appels aléatoires comme indiqué dans les commentaires. Un exemple :

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

4voto

Antonio Points 2246

Je remplirais à l'avance un tampon (circulaire) (suffisamment long) de valeurs aléatoires de 64 bits, puis je prendrais très rapidement un bit à la fois lorsque j'aurais besoin d'une valeur aléatoire booléenne.

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

Bien entendu, la classe peut être généralisée à la taille du tampon, au générateur aléatoire, au type de base (qui ne doit pas nécessairement être uint64_t), etc.


Accès au tampon une seule fois tous les 64 appels :

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

3voto

Peter Points 1765

À moins que vous n'ayez d'autres contraintes sur le caractère aléatoire dont vous avez besoin, la façon la plus rapide de générer un bool aléatoire est :

bool RandomBool() { return false; }

Pour être plus précis, il existe des milliers de façons de générer des nombres booléens aléatoires, toutes répondant à des contraintes différentes, et beaucoup d'entre elles ne fournissent pas de nombres "véritablement" aléatoires (cela inclut toutes les autres réponses données jusqu'à présent). Le mot "aléatoire" seul n'indique pas les propriétés dont vous avez réellement besoin.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X