2011-07-05 9 views
12

std :: random_shuffleはスレッドセーフですか?通常のrand()はスレッドセーフではないので、私は推測しません。そうであれば、どうやってrand_rをrandom_shuffleと一緒に使うと、各スレッドに一意のシードを与えることができます。私はrandom_shuffleでカスタムランダムジェネレータを使用する例を見てきましたが、それはまだ私には不透明です。random_shuffleはスレッドセーフですか?そうでない場合はrand_rを使用する

ありがとうございました。

+4

class rand_x { unsigned int seed; public: rand_x(int init) : seed(init) {} int operator()(int limit) { int divisor = RAND_MAX/(limit+1); int retval; do { retval = rand_r(&seed)/divisor; } while (retval > limit); return retval; } }; 

をあなたのようなrandom_shuffle何かでそれを使用したいですドキュメントに別途記載がない限り、スレッドセーフです。 –

+1

また、 'threadsafe'は非常にオーバーロードされた用語です。アルゴリズムによっては、安全なデータを扱う場合にのみ安全です。ライターが1人しかいない限り、スレッド間で安全なものもあり、そのほとんどは保証できません。一般に、何が安全であるか(すなわち、正しい)を決めるときは、さまざまな読み取り/書き込み要件を指定する必要があります。 – Kylotan

+0

明確にするために、私は並行して別のリストでシャッフルをしたい。だから私はデータ構造の競争、シャッフル用の乱数の生成だけに心配していません。 – Mark

答えて

4

std::random_shufflerand_rを使用するには、(些細な)ラッパーを記述する必要があります。 random_shuffleに渡す乱数ジェネレータは、生成する数値の範囲を指定するパラメータを受け入れる必要があります。rand_rは生成しません。

あなたのラッパーは次のようになります。一般的に、あなたがC++ライブラリ内の*何も*ない仮定があることを確認しなければならない

std::random_shuffle(whatever.begin(), whatever.end(), rand_x(some_seed)); 
+0

ありがとうございます。シードは符号なしintでなければなりません。また、なぜrand_r(&seed)%limitを返すのとは対照的にdo whileループがありますか?私が行方不明の微妙なものがありますか? – Mark

+0

@マーク:ありがとうございました。 doループについては、3人の子供の間で10個のキャンディーを均等に分割する方法を考えてください(1個のキャンディーを分割することはできません)。答えはあなたができないということです。あなたはそのうちの9つしか配布できません。これは基本的に、 'RAND_MAX'キャンディーを' limit'子供の間で分割し、すべてのファイルが等しくなるように残ったものをすべて破棄するのと同じことをしています。 '%limit(または'/divisor')を単独で使うと、 'RAND_MAX'が' limit'の正確な倍数にならない限り、数値を均等に分割することはできません( 'RAND_MAX'はしばしば素数です。 *任意の*意味のある 'limit'の正確な倍数)。 –

+0

私はJerryが言ったすべてに同意しますが、もしあなたが 'rand_r'を使っているなら、あなたはそれが保存するために一様な分布を持っていると仮定する権利がないと主張するかもしれません。しかし、少なくともこのように、もし 'rand_r'が良いなら、あなたのシャッフルも良いです、あなたは新しいバイアスを導入していません。 –

3

整数値型を取り、関数に渡したイテレータがコンテナの境界をオーバーフローしない整数型の別の値を返す乱数ジェネレータ関数またはファンクタオブジェクトを用意する必要があります繰り返す。また、ファンクタオブジェクトの場合は、関数のように呼び出すことができるように、operator()を実装する必要があります。スレッド安全な乱数ジェネレータが必要なので、srandrandからcstdlibまでは悪い考えです...代わりに、スレッドセーフな乱数ジェネレータ、または乱数ジェネレータを実装するファンクタオブジェクトを作成する必要があります。グローバルにアクセス可能な変数を実装していないため、すべてがスレッドローカルな記憶域になります。

例えば、これがうまくいく方法の1つは、固定範囲の値の間にランダムな値しか生成しない別のライブラリから取得したいくつかのタイプの乱数ジェネレータを持つことです。したがって、コンテナの境界を定義できますランダムアクセスイテレータの場合はrandom_shuffleアルゴリズムが使用されます。今、あなたはファンクタは、以下のようになります、あなたが使用しているもののライブラリに依存する:

class my_rand_gen 
{ 
    private: 
     random_gen_type random_range_gen; 
     int min; 
     int max; 

    public: 
     my_rand_gen(const random_gen_type& gen, int min_range, int max_range): 
        random_range_gen(gen), min(min_range), max(max_range) {} 

     int operator()(int value) 
     { 
      //ignore the input value and use our own defined range 
      //returns a value between min and max 
      return random_range_gen(min, max); 
     } 
}; 

今、あなたは次のようなアルゴリズムを呼び出すことができます。

random_shuffle(my_vector_start_iter, my_vector_end_iter, 
       my_rand_gen(rand_generator_lib, 
          vector_start_index, 
          vector_end_index)); 

、それがで-間の開始ベクトルをシャッフルしますベクトルの境界をオーバーフローせずにベクトルにイテレータを終了させることができます。つまり、vector_start_indexvector_end_indexの間のシャッフルの値のみを使用します。

+0

新しい' 'クラスが良いスタートになるだろう。スレッドごとに1つのPRNG。おかげさまで、 –

1

おそらくそうではありません。

乱数ジェネレータのテンプレートパラメータ:http://www.sgi.com/tech/stl/random_shuffle.htmlを使用するadnrom_shuffleの2番目のバージョンを使用します。発電機が一致している必要があります:http://www.sgi.com/tech/stl/RandomNumberGenerator.html

struct MyRandomNumberGenerator 
{ 
    int operator()(int limit) const 
    { 
     // get threadsafe random number 
    } 
}; 

// Stuff 
random_shuffle(list.begin(), list.end(), MyRandomNumberGenerator());