web-dev-qa-db-ja.com

C ++ 11標準ライブラリを使用して乱数を生成するにはどうすればよいですか

新しいC++ 11標準には、乱数ジェネレーター専用の章全体があります。しかし、標準Cライブラリに頼らずに、このようにコーディングされていた最も単純で最も一般的なタスクを実行するにはどうすればよいですか。

srand((unsigned int)time(0)); 
 int i = Rand();

箱から出して使用できる乱数エンジン、ディストリビューション、シードの適切なデフォルトはありますか?

23

次のようなことができるはずです。

std::default_random_engine e((unsigned int)time(0));
int i = e();

default_random_engineの品質は実装に依存します。 std::min_Rand0またはstd::min_Randを使用することもできます。

おそらく、ランダムエンジンをシードするためのより良い方法は、timeを使用するのではなく、実装から利用できるのと同じくらい真の乱数を使用することです。

例えば。

std::random_device rd;
std::default_random_engine e( rd() );
30
CB Bailey

すでに提供されているサンプルのいくつかを統合して単純化することを要約します。

// Good random seed, good engine
auto rnd1 = std::mt19937(std::random_device{}());

// Good random seed, default engine
auto rnd2 = std::default_random_engine(std::random_device{}());

// like rnd1, but force distribution to int32_t range
auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}()));

// like rnd3, but force distribution across negative numbers as well
auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));

次に、いくつかのテストを実行して、デフォルトがどのように見えるかを確認しました。

#include <random>
#include <functional>
#include <limits>
#include <iostream>

template<class Func>
void print_min_mean_max(Func f) {
   typedef decltype(f()) ret_t;
   ret_t min = std::numeric_limits<ret_t>::max(), max = std::numeric_limits<ret_t>::min();
   uint64_t total = 0, count = 10000000;
   for (uint64_t i = 0; i < count; ++i) {
      auto res = f();
      min = std::min(min,res);
      max = std::max(max,res);
      total += res;
   }
   std::cout << "min: " << min << " mean: " << (total/count) << " max: " << max << std::endl;
}

int main() {
   auto rnd1 = std::mt19937(std::random_device{}());
   auto rnd2 = std::default_random_engine(std::random_device{}());

   auto rnd3 = std::bind(std::uniform_int_distribution<int32_t>{}, std::mt19937(std::random_device{}()));
   auto rnd4 = std::bind(std::uniform_int_distribution<int32_t>{std::numeric_limits<int32_t>::min(),std::numeric_limits<int32_t>::max()}, std::mt19937(std::random_device{}()));

   print_min_mean_max(rnd1);
   print_min_mean_max(rnd2);
   print_min_mean_max(rnd3);
   print_min_mean_max(rnd4);
}

出力を生成します:

min: 234 mean: 2147328297 max: 4294966759
min: 349 mean: 1073305503 max: 2147483423
min: 601 mean: 1073779123 max: 2147483022
min: -2147481965 mean: 178496 max: 2147482978

ご覧のとおり、mt19937とdefault_random_engineのデフォルト範囲は異なるため、uniform_int_distributionを使用することをお勧めします。

また、符号付き整数型を使用している場合でも、デフォルトのUniform_int_distributionは[0、max_int](負ではない)です。全範囲が必要な場合は、範囲を明示的に指定する必要があります。

最後に、 これを覚えておくことが重要です このようなとき。

6
mmocny

私のプロジェクトでは次のコードを使用しています。 「engine」と「distribution」は、ライブラリによって提供されるものの1つにすることができます。

#include <random>
#include <functional>
#include <iostream>
...
std::uniform_int_distribution<unsigned int> unif;
std::random_device rd;
std::mt19937 engine(rd());
std::function<unsigned int()> rnd = std::bind(unif, engine);

std::cout << rnd() << '\n';
2
dimitri

どうぞ。ランダムは次の範囲で2倍になります。

// For ints
// replace _real_ with _int_, 
// <double> with <int> and use integer constants

#include <random>
#include <iostream>
#include <ctime>
#include <algorithm>
#include <iterator>

int main()
{
    std::default_random_engine rng(std::random_device{}()); 
    std::uniform_real_distribution<double> dist(-100, 100);  //(min, max)

    //get one
    const double random_num = dist(rng);

    //or..
    //print 10 of them, for fun.
    std::generate_n( 
        std::ostream_iterator<double>(std::cout, "\n"), 
        10, 
        [&]{ return dist(rng);} ); 
    return 0;
}
2
Carl

既存のコードが新しい標準の前に適切であった場合、それは引き続き適切です。新しい乱数ジェネレーターは、より高品質の疑似ランダム性を必要とするアプリケーション用に追加されました。確率シミュレーション。

2
David Heffernan

RC4を使用してランダムバイトを生成できます。これはおそらくあなたが望む特性を持っています。実装は高速でかなり簡単です。シードがわかっている場合、シーケンスはすべての実装で再現可能であり、シードが不明な場合は完全に予測できません。 http://en.wikipedia.org/wiki/RC4

0
Matt Mahoney

乱数の生成は難しい問題です。それを行うための本当にランダムな方法はありません。ゲーム環境をシードするためにランダム性を生成しているだけの場合は、アプローチは問題ないはずです。 Rand()にはいくつかの欠点があります。

暗号化キーを生成するためにランダム性が必要な場合は、S.O.Lです。その場合の最良の方法は、通常はメカニズムを備えたオペレーティングシステムにアクセスすることです。 POSIXでは、それはrandom()です(または、そのように処分されている場合は/ dev/randomから読み取ります)。 Windowsでは、CryptoAPIを使用できます。

https://www.securecoding.cert.org/confluence/display/seccode/MSC30-C.+Do+not+use+the+Rand%28%29+function+for+generate+pseudorandom+numbers

0
Adam Hawes