Инкапсуляция boost :: random для простоты использования вместо rand () - PullRequest
3 голосов
/ 01 сентября 2010

для моей программы мне нужны псевдослучайные числа с разными диапазонами. До сих пор я использовал функцию rand (), но у нее есть свои ограничения.

Я обнаружил, что библиотека boost :: random - гораздо лучшая замена, но я не хотел создавать случайные генераторы повсюду.
(Мне нужны случайные целые числа во многих классах, потому что это программное обеспечение для стресс-тестирования, которое принимает каждое решение псевдослучайно (-> тестовый прогон должен повторяться, устанавливая одинаковое начальное начальное число)).

Вот почему я учился в классе boost :: random в моем собственном классе.

Идея, стоящая за этим, заключается в том, чтобы облегчить использование, чтобы оно было почти таким же простым, как метод C ++ rand ()

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

Генерация случайного числа в заданном диапазоне теперь будет так же проста, как и

#include "Random.h"
  Random::Get()->SetSeed( 123123 );  // If you want to make the run repeatable
  int dice = Random::Get()->Random(1,6);

Вопрос:
Что-то не так с этим способом генерации случайных чисел?
Большие накладные расходы я не узнал?
Чистое зло или устаревшая методика программирования?

(Я все еще новичок в C ++ и хочу улучшить свои навыки, и я обнаружил, что переполнение стека лучшее место, чтобы получить качественную консультацию)

Ответы [ 6 ]

3 голосов
/ 01 сентября 2010

Джо Готерин продемонстрировал проблему, однако не предложил никакого решения:)

Проблема с общим состоянием заключается в отсутствии повторного входа: то есть, выполнение дважды одного и того же метода не дает тот же результат.Это особенно важно в многопоточных ситуациях, потому что глобальное состояние не всегда может изменяться в одной и той же точке программы, что приводит к противоречивым результатам при каждом запуске.

Решение состоит в том, что каждое моделирование должно иметь свое собственное«состояние», и тогда вы избежите общего состояния.

Это может быть достигнуто несколькими способами: вы все еще можете использовать «глобальное» состояние, но сделать его локальным для потока, например, таким образом,потоки не будут наступать друг другу на ноги.

Более чистая версия, однако, состоит в том, чтобы хранить это состояние где-то, и более простой способ - иметь некоторый класс Context, который создается один раз за симуляцию и которыйявляется агрегатом состояния симуляции (для состояния всей симуляции).

Имея это в виду:

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

Затем передайте экземпляр Context в вашей симуляции,и вы можете запустить столько, сколько вы хотите параллельно.

3 голосов
/ 01 сентября 2010

Вы по сути обернули свой генератор в синглтон , представляя все проблемы, которые несут одиночные и глобальные переменные. Например, у вас могут возникнуть проблемы с параллельным выполнением нескольких стресс-тестов, поскольку ваша реализация не является поточно-ориентированной.

Но главная проблема, которую я вижу, заключается в том, что ваша оболочка не проще, чем просто использовать boost :: random без оболочки.

1 голос
/ 02 ноября 2010

Ниже моя версия инкапсуляции:

#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();
}
1 голос
/ 01 сентября 2010

Возможно, вы могли бы избежать Get().Это чисто субъективно, для меня.Я бы предпочел механизм вызова типа Random::Seed() и Random::Next() или Random::Next(min,max).В Random не так уж много функций, поэтому вы можете сделать их статическими.

Вот простая реализация.Но имейте в виду, что это с учетом того, что вы используете это в однопоточной среде.Для многопоточной среды лучше не использовать его в качестве одиночного.

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;
0 голосов
/ 01 сентября 2010

Вы также можете попробовать что-то вроде создания сущности в контейнере и случайным образом перемешать их.

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());
}
0 голосов
/ 01 сентября 2010

Я бы сказал, что это выглядит хорошо - таким образом, вы можете легко заменить ваш алгоритм случайного поколения, если он понадобится.

...