封装 boost::random 以便于使用来替换 rand()

发布于 2024-09-16 11:57:09 字数 1636 浏览 2 评论 0原文

对于我的程序,我需要具有不同范围的伪随机整数。 到目前为止,我使用 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++ 新手,想提高我的技能,我发现堆栈溢出是 获得高质量建议的最佳地点)

for my program I need pseudo random integers with different ranges.
Until now I used the rand() function but it has it's limitations.

I found the boost::random library to be a much better replacement but I didn't want to create random generators all over the place.
( I need random integers in many classes, because it's a stress test software that makes every decision pseudo-randomly ( -> a test run has to be repeatable by setting the same start seed ) ).

That's why I capsuled boost::random away in my own class.

The idea behind this is to ease the use so that it is almost as straightforward as
the C++ rand() method

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

Generating a random number within a given range will now be as easy as

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

Question:
Is there anything wrong with this way of generating random numbers?
Large overhead I didn't recognize ?
Pure Evil or outdated programming technique ?

( I'm still new to c++ and want to improve my skills, and I have found that Stack overflow is
the best place to get high quality advice )

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

梦里南柯 2024-09-23 11:57:09

Joe Gauterin 演示了这个问题,但是它没有提供任何解决方案:)

共享状态的问题是缺乏重入:即,执行两次相同的方法不会提供相同的结果。这在多线程情况下尤其重要,因为全局状态可能并不总是在程序中的同一点发生变化,从而导致一次运行到另一次运行的结果不一致。

解决方案是每个模拟都应该有自己的“状态”,然后您就可以避免共享状态。

这可以通过多种方式来完成:例如,您仍然可以使用“全局”状态,但将其设置为线程的本地状态,这样线程就不会踩到彼此的脚趾。

然而,更干净的版本在于将此状态存储在某处,更简单的方法是使用某种 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 实例,您就可以并行运行任意数量的实例。

Joe Gauterin demonstrated the issue, however it didn't offered any solution :)

The problem with shared state is the absence of reentrance: ie, executing twice the same method does not provide the same result. This is particularly critical in multithreaded situations because the global state may not always change at the same point in the program, thus leading to inconsistent results from one run to another.

The solution is that each simulation should have its own "state" and then you would avoid the shared state.

This can be accomplished in a number of ways: you could still use a "global" state but make it local to a thread, for example, thus the threads would not step on each others toes.

The cleaner version, however, consists in storing this state somewhere, and the easier way is to have some kind of Context class, instantiated once per simulation, and which is an aggregate of the state of the simulation (for simulation-wide state).

With that in mind:

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

Then, pass the Context instance around in your simulation, and you can run as many as you wish in parallel.

你又不是我 2024-09-23 11:57:09

您基本上已经将生成器包装在 单例 中,引入了单例和全局变量的所有问题携带。例如,您很难并行运行多个压力测试,因为您的实现不是线程安全的。

但我看到的主要问题是您的包装器并不比仅使用 boost::random 而不使用包装器简单。

You've essentially wrapped your generator in a singleton, introducing all the problems that singletons and global variables carry. For example, you'd have difficulty getting multiple stress tests running in parallel, as your implementation isn't thread safe.

But the main problem that I see is that your wrapper isn't simpler than just using boost::random without the wrapper.

娇纵 2024-09-23 11:57:09

您或许可以避免 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;

You could probably avoid Get(). This is purely subjective, to me. I would prefer a calling mechanism like Random::Seed() and Random::Next() or Random::Next(min,max). There aren't too many function in Random so you could make them all static functions.

Here's a simple implementation. But bear in mind that this is considering you're using this in a single-threaded environment. For multi-threaded environment it's better to not have it as a 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;
離殇 2024-09-23 11:57:09

以下是我的封装版本:

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

Below is my version of encapsulation:

#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();
}
乖乖 2024-09-23 11:57:09

我想说这看起来不错——这样您就可以在需要时轻松替换随机生成算法。

I'd say this looks fine -- this way you can easily replace your random generation algo should it be needed.

总以为 2024-09-23 11:57:09

您还可以尝试在容器中创建实体并随机洗牌之类的操作。

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

You can also try something like create entity in a container and random shuffle them.

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());
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文