Encapsulamento boost::aleatório para facilidade de uso para substituir rand()
-
26-09-2019 - |
Pergunta
para o meu programa eu preciso de pseudo números inteiros aleatórios com intervalos diferentes.Até agora eu usei a função aleatório (), mas ele tem suas limitações.
Eu achei o boost::aleatório biblioteca de ser muito melhor substituição, mas eu não queria criar geradores aleatórios em todo o lugar.
( Eu preciso de números inteiros aleatórios em muitas classes, porque é um software de teste de estresse que faz com que cada decisão de forma pseudo-aleatória ( -> executar um teste tem de ser repetitivo, definindo a mesma semente ) ).
É por isso que eu capsuled boost::aleatório de distância, na minha própria classe.
A idéia por trás disso é a facilidade do uso, de modo que é quase tão simples como o C++ rand() método
#include "boost/shared_ptr.hpp"
#include "boost/random.hpp"
class Random{
public:
typedef boost::shared_ptr< Random > randomPtr;
typedef boost::mt19937 randomGeneratorType;
static randomPtr Get(){
static randomPtr randomGen( new RandomGenerator() );
return randomGen;
}
void SetSeed(int seed){
randomGenerator.seed( seed );
}
int Random( int lowerLimit, int upperLimit ){
boost::uniform_int<> distribution( lowerLimit, upperLimit );
boost::variate_generator< randomGeneratorType&, boost::uniform_int<> >
LimitedInt( randomGenerator , distribution );
return LimitedInt();
}
private:
// prevent creation of more than one object of the LogManager class
// use the Get() method to get a shared_ptr to the object
Random():
randomGenerator() //initialize randomGenerator with default constructor
{}
RandomGenerator( const RandomGenerator& orig ){};
randomGeneratorType randomGenerator;
};
A geração de um número aleatório dentro de um determinado intervalo agora vai ser tão fácil como
#include "Random.h"
Random::Get()->SetSeed( 123123 ); // If you want to make the run repeatable
int dice = Random::Get()->Random(1,6);
Pergunta:
Há algo de errado com esta forma de geração de números aleatórios?
Grande sobrecarga eu não reconheço ?
Pura Maldade ou desatualizados técnica de programação ?
( Eu ainda sou novo para c++ e quer melhorar minhas habilidades, e eu descobri que o estouro de Pilha é o melhor lugar para obter alta qualidade e aconselhamento )
Solução
Joe Gauterin demonstrou o problema, no entanto, não ofereceu nenhuma solução :)
O problema com o estado compartilhado é a ausência de reentrada: ou seja, executar o dobro do mesmo método não fornece o mesmo resultado. Isso é particularmente crítico em situações multithreads, porque o estado global nem sempre muda no mesmo ponto do programa, levando a resultados inconsistentes de uma execução para outra.
A solução é que cada simulação deve ter seu próprio "estado" e você evitará o estado compartilhado.
Isso pode ser realizado de várias maneiras: você ainda pode usar um estado "global", mas torná -lo local para um segmento, por exemplo, portanto, os threads não pisariam nos dedos um do outro.
A versão mais limpa, no entanto, consiste em armazenar esse estado em algum lugar, e a maneira mais fácil é ter algum tipo de Context
classe, instanciada uma vez por simulação, e que é um agregado do estado da simulação (para o estado em toda a simulação).
Com aquilo em mente:
class Context
{
public:
typedef boost::mt19937 RandomGeneratorType;
void SetSeed(int seed){
rg.seed( seed );
}
int Next( int lowerLimit, int upperLimit ) {
boost::uniform_int<> distribution( lowerLimit, upperLimit );
boost::variate_generator< randomGeneratorType&, boost::uniform_int<> >
LimitedInt( rg, distribution );
return LimitedInt();
}
private:
RandomGeneratorType rg;
};
Então, passe o Context
Instância em sua simulação e você pode executar o máximo que desejar em paralelo.
Outras dicas
Você basicamente envolto seu gerador em um singleton, a introdução de todos os problemas que singletons e variáveis globais transportar.Por exemplo, você teria dificuldade para obtenção de vários testes de estresse executando em paralelo, como a sua implementação não é thread-safe.
Mas o principal problema que eu vejo é que o seu invólucro, não mais simples do que apenas usando o boost::aleatório, sem embalagem.
Você provavelmente poderia evitar Get()
. Isso é puramente subjetivo, para mim. Eu preferiria um mecanismo de chamada como Random::Seed()
e Random::Next()
ou Random::Next(min,max)
. Não há muita função aleatória, então você pode torná -los todas as funções estáticas.
Aqui está uma implementação simples. Mas lembre-se de que isso está considerando que você está usando isso em um ambiente de thread único. Para um ambiente multithread, é melhor não tê-lo como um singleton.
class Random
{
public:
typedef boost::mt19937 RandomGeneratorType;
static void Seed(int seed)
{
s_randGen.seed(seed);
}
static int NextInt(int min_val, int max_val)
{
boost::uniform_int<> distribution(min_val, max_val);boost::variate_generator< randomGeneratorType&, boost::uniform_int<> >
return LimitedInt( s_randGen , distribution );;
}
private:
static RandomGeneratorType s_randGen;
};
Random::RandomGeneratorType Random::s_randGen;
Abaixo está minha versão do encapsulamento:
#include <boost/random.hpp>
#include <ctime>
int getRandomIntValue(int min, int max)
{
static boost::minstd_rand gen((unsigned int)std::time(NULL));
boost::uniform_int<int> dist(min, max);
boost::variate_generator<
boost::minstd_rand&,
boost::uniform_int<int>> combgen(gen, dist);
return combgen();
}
Eu diria que isso parece bom - dessa maneira você pode substituir facilmente seu algo de geração aleatória, caso seja necessário.
Você também pode tentar algo como criar entidade em um contêiner e embaralhar aleatório.
void
GenerateRandomString(vector<string>& container,
int size_of_string,
unsigned long long num_of_records,
int thread_id)
{
srandom(time(0));
random();
for(unsigned long long int i=0; i < num_of_records; ++i)
{
stringstream str_stream;
str_stream.clear();
str_stream << left << setfill('x') << setw(size_of_string-4);
str_stream << num_of_records+i+1 << "-" << thread_id;
container.push_back(str_stream.str());
}
random_shuffle(container.begin(), container.end());
}