rand() を置き換えるために使いやすくするために boost::random をカプセル化する
-
26-09-2019 - |
質問
私のプログラムでは、異なる範囲の擬似ランダム整数が必要です。これまでは 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 ++を初めて使用していて、自分のスキルを向上させたいと思っています。スタックオーバーフローが高品質のアドバイスを得るのに最適な場所であることがわかりました)
解決
Joe Gauterin はこの問題を実証しましたが、解決策は何も提供されませんでした:)
共有状態の問題は、再入ができないことです。つまり、同じメソッドを 2 回実行しても、同じ結果は得られません。これは、マルチスレッドの状況では特に重要です。グローバル状態はプログラムの同じ時点で常に変更されるとは限らず、実行ごとに結果が一貫しないことになるからです。
解決策は、各シミュレーションが独自の「状態」を持つ必要があり、共有状態を避けることです。
これはさまざまな方法で実現できます。「グローバル」状態を使用しながら、それをスレッドに対してローカルにすることもできます。これにより、スレッドが互いに足を踏み入れることがなくなります。
ただし、よりクリーンなバージョンは、この状態をどこかに保存することで構成されており、より簡単な方法は、ある種の Context
クラス。シミュレーションごとに 1 回インスタンス化され、シミュレーションの状態 (シミュレーション全体の状態の場合) の集合体です。
それを念頭に置いて:
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
シミュレーション内でインスタンスを実行でき、必要なだけ並列実行できます。
他のヒント
基本的にジェネレーターをラップしたことになります。 シングルトン, シングルトンとグローバル変数が抱えるすべての問題を紹介します。たとえば、実装がスレッド セーフではないため、複数のストレス テストを並行して実行するのは困難です。
しかし、私が見た主な問題は、ラッパーが単にラッパーなしで boost::random を使用するほど単純ではないということです。
おそらく避けられるでしょう 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;
以下は私のバージョンのカプセル化です。
#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();
}
これは問題ないようです。こうすることで、必要に応じてランダム生成アルゴリズムを簡単に置き換えることができます。
コンテナー内にエンティティを作成し、それらをランダムにシャッフルするなどのことを試すこともできます。
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());
}