C ++ 11ランダムライブラリを使用して乱数を生成する


135

タイトルが示すように、私は新しいC ++ 11 <random>ライブラリを使用して乱数を生成する方法を理解しようとしています。私はこのコードでそれを試しました:

std::default_random_engine generator;
std::uniform_real_distribution<double> uniform_distance(1, 10.001);

私が持っているコードの問題は、コンパイルして実行するたびに、常に同じ数が生成されることです。だから私の質問は、ランダムライブラリの他の関数が本当にランダムでありながらこれを達成できるのですか?

私の特定の使用例では、範囲内の値を取得しようとしていました [1, 10]


3
この質問は、「主に意見ベース」に危険なほど境界を接しています。意見の募集を取り除くことができれば、この質問は非常に便利です(まだ質問されていない場合)。
John Dibling 2013年

4
std::mt19937理由がない限り、エンジンとしてa を使用することをお勧めします。そして、分布は両端が閉じた間隔です。
chris


2
@chris配布は両端で閉じられていません。このリンクまたはこのリンクを
memo1288

1
@ memo1288、ありがとうございます。OPは両端閉じているstd::uniform_int_distributionを使用していると思いました。
クリス2013年

回答:


191

MicrosoftのStephan T. Lavavej(stl)が、Going Nativeで、新しいC ++ 11ランダム関数の使用方法と、なぜ使用しないのかについて講演しましたrand()。その中に、彼は基本的にあなたの質問を解決するスライドを含めました。下のスライドからコードをコピーしました。

ここで彼の完全な講演を見ることができます:http : //channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

#include <random>
#include <iostream>

int main() {
    std::random_device rd;
    std::mt19937 mt(rd());
    std::uniform_real_distribution<double> dist(1.0, 10.0);

    for (int i=0; i<16; ++i)
        std::cout << dist(mt) << "\n";
}

random_device1回使用して、という名前の乱数ジェネレータをシードしmtます。random_device()は低速ですがmt19937、オペレーティングシステムからランダムなデータを要求するため、シードする必要はありません(RdRandなどのさまざまな場所からソースが提供されます)。


この質問/回答を見るとuniform_real_distribution、範囲内の数値が返される[a, b)ことがわかり[a, b]ます。これを行うには、uniform_real_distibution実際には次のようになります。

std::uniform_real_distribution<double> dist(1, std::nextafter(10, DBL_MAX));

3
質問はあなたが使いたいかもしれない乱数を生成する最も一般的な方法を求めているので、default_random_enginec ++のプライマーによると、それは実装が最も有用であると考えたものです
aaronman

2
@aaronman:私はSTLの話を聞きますが、彼は明らかにそれが嫌いですdefault_random_engine
Bill Lynch

5
@chris私たちは皆、ベクトルとマップの違いを知っていますが、mt19937とranlux24の違いを誰もが知っているstd::default_containerわけではありません。違いを知らないプログラマを考えている人々、多くのスクリプト言語にはデフォルトのマップタイプ構造があり、ユーザーが知らないさまざまな方法で実装できます
aaronman

21
このnextafter呼び出しは、ほとんどのアプリケーションにとって過剰です。doubleエンドポイントに正確にランダムに着地する可能性は非常に小さいので、それを含めることと除外することの間に実際的な違いはありません。
Mark Ransom 2014年

3
@chris無関係(ただし、ドアを開けた)、std::vectorここでのアナロジーは機能しません。CPUキャッシングのため、これstd::vector 実際には適切なデフォルトであるためです。std::list中央に挿入するよりも優れています。これは、すべてのコンテナーを理解していて、アルゴリズムの複雑さに基づいて情報に基づいた決定を下すことができる場合でも当てはまります。
void.pointer 2014年

24

私の「ランダム」ライブラリは、C ++ 11ランダムクラスの非常に便利なラッパーを提供します。簡単な「get」メソッドでほとんどすべてのことができます。

例:

  1. 範囲内の乱数

    auto val = Random::get(-10, 10); // Integer
    auto val = Random::get(10.f, -10.f); // Float point
  2. ランダムブール

    auto val = Random::get<bool>( ) // 50% to generate true
    auto val = Random::get<bool>( 0.7 ) // 70% to generate true
  3. std :: initilizer_listからのランダムな値

    auto val = Random::get( { 1, 3, 5, 7, 9 } ); // val = 1 or 3 or...
  4. イテレータ範囲またはすべてのコンテナからのランダムなイテレータ

    auto it = Random::get( vec.begin(), vec.end() ); // it = random iterator
    auto it = Random::get( vec ); // return random iterator

そして、もっともっと!githubページをチェックしてください:

https://github.com/effolkronium/random


4

私は、上記のすべてのものを赤のようなことではC ++で40件の他のページについて、このと見ステファン・T. Lavavejからビデオを「STL」 とまだ私は把握して完全な日曜日かかったので実践で確認してくださいどのように乱数の作品ではなかったですそのすべてについて、それがどのように機能し、使用できるか。

私の意見では、STLは「もうsrandを使わない」ことについて正しいと彼はビデオでそれをうまく説明しました2。彼はまた使用することをお勧めします:

a)void random_device_uniform()-暗号化された生成が遅い(私の例から)

b)以下の例mt19937-高速で、暗号化されていないシードを作成する機能


私がアクセスできるすべての主張されたc ++ 11の本を取り出したところ、Breymann(2015)のようなドイツの作者がまだクローンを使用していることがわかりました

srand( time( 0 ) );
srand( static_cast<unsigned int>(time(nullptr))); or
srand( static_cast<unsigned int>(time(NULL))); or

ちょうどで<random>はなく、<time> and <cstdlib>#includings -そう:)だけで一冊の本から学ぶように注意してください。

意味-c ++ 11以降は使用しないでください:

多くの場合、プログラムは乱数のソースを必要とします。新しい標準の前は、CとC ++の両方がrandという名前の単純なCライブラリ関数に依存していました。その関数は、0からシステムに依存する最大値(少なくとも32767)の範囲で均一に分布する疑似乱数整数を生成します。rand関数には、いくつかの問題があります。 randがプロデュースしたもの。一部のアプリケーションでは、ランダムな浮動小数点数が必要です。一部のプログラムでは、不均一な分布を反映する数値が必要です。プログラマは、randによって生成される数値の範囲、型、または分布を変換しようとするときに、非ランダム性を導入することがよくあります。(Lippmans C ++プライマー第5版2012からの引用)


私はついに、Bjarne Stroustrupsの新しい20冊の本から最良の説明を見つけました-彼は彼のことを知っているはずです-「C ++ 2019のツアー」、「C ++ 2016を使用したプログラミングの原則と実践」、および「C ++プログラミング言語第4版」 2014年」と「リップマンズC ++プライマー第5版2012」の例:

乱数ジェネレーターは2つの部分で構成されているため、非常に単純です。 (1)ランダムまたは疑似ランダム値のシーケンスを生成するエンジン。(2)これらの値を範囲内の数学的分布にマップする分布。


MicrosoftのSTLの人の意見にもかかわらず、Bjarne Stroustrupsは次のように書いています。

では、標準ライブラリが乱数エンジンと分布を提供しています(§24.7)。デフォルトではdefault_random_engineを使用します。これは、幅広い適用性と低コストのために選択されています。

このvoid die_roll()例は、Bjarne Stroustrupsによるものです。優れたアイデア生成エンジンとディストリビューションusing (詳細はこちら)


ここで、標準ライブラリによって提供される乱数ジェネレータを実際に使用できるようにするには、<random> さまざまな例を含む実行可能コードを必要最小限に減らし、時間とお金を安全にしてください。

    #include <random>     //random engine, random distribution
    #include <iostream>   //cout
    #include <functional> //to use bind

    using namespace std;


    void space() //for visibility reasons if you execute the stuff
    {
       cout << "\n" << endl;
       for (int i = 0; i < 20; ++i)
       cout << "###";
       cout << "\n" << endl;
    }

    void uniform_default()
    {
    // uniformly distributed from 0 to 6 inclusive
        uniform_int_distribution<size_t> u (0, 6);
        default_random_engine e;  // generates unsigned random integers

    for (size_t i = 0; i < 10; ++i)
        // u uses e as a source of numbers
        // each call returns a uniformly distributed value in the specified range
        cout << u(e) << " ";
    }

    void random_device_uniform()
    {
         space();
         cout << "random device & uniform_int_distribution" << endl;

         random_device engn;
         uniform_int_distribution<size_t> dist(1, 6);

         for (int i=0; i<10; ++i)
         cout << dist(engn) << ' ';
    }

    void die_roll()
    {
        space();
        cout << "default_random_engine and Uniform_int_distribution" << endl;

    using my_engine = default_random_engine;
    using my_distribution = uniform_int_distribution<size_t>;

        my_engine rd {};
        my_distribution one_to_six {1, 6};

        auto die = bind(one_to_six,rd); // the default engine    for (int i = 0; i<10; ++i)

        for (int i = 0; i <10; ++i)
        cout << die() << ' ';

    }


    void uniform_default_int()
    {
       space();
       cout << "uniform default int" << endl;

       default_random_engine engn;
       uniform_int_distribution<size_t> dist(1, 6);

        for (int i = 0; i<10; ++i)
        cout << dist(engn) << ' ';
    }

    void mersenne_twister_engine_seed()
    {
        space();
        cout << "mersenne twister engine with seed 1234" << endl;

        //mt19937 dist (1234);  //for 32 bit systems
        mt19937_64 dist (1234); //for 64 bit systems

        for (int i = 0; i<10; ++i)
        cout << dist() << ' ';
    }


    void random_seed_mt19937_2()
    {
        space();
        cout << "mersenne twister split up in two with seed 1234" << endl;

        mt19937 dist(1234);
        mt19937 engn(dist);

        for (int i = 0; i < 10; ++i)
        cout << dist() << ' ';

        cout << endl;

        for (int j = 0; j < 10; ++j)
        cout << engn() << ' ';
    }



    int main()
    {
            uniform_default(); 
            random_device_uniform();
            die_roll();
            random_device_uniform();
            mersenne_twister_engine_seed();
            random_seed_mt19937_2();
        return 0;
    }

私はそれをすべて追加すると思います、そして私が言ったように、それをその例に蒸留するために私にたくさんの読書と時間を要しました-あなたが番号生成についてさらに何かがあるなら、私は午後またはコメントセクションでそれについて聞いてうれしいです必要に応じて追加するか、この投稿を編集します。ブール


0

ここに私がそれらの行に沿って書いたものがあります::

#include <random>
#include <chrono>
#include <thread>

using namespace std;

//==============================================================
// RANDOM BACKOFF TIME
//==============================================================
class backoff_time_t {
  public:
    random_device                      rd;
    mt19937                            mt;
    uniform_real_distribution<double>  dist;

    backoff_time_t() : rd{}, mt{rd()}, dist{0.5, 1.5} {}

    double rand() {
      return dist(mt);
    }
};

thread_local backoff_time_t backoff_time;


int main(int argc, char** argv) {
   double x1 = backoff_time.rand();
   double x2 = backoff_time.rand();
   double x3 = backoff_time.rand();
   double x4 = backoff_time.rand();
   return 0;
}


0

ここに、疑似乱数ジェネレータについて読むことができるいくつかのリソースがあります。

https://en.wikipedia.org/wiki/Pseudorandom_number_generator

基本的に、コンピューターの乱数にはシードが必要です(この数は現在のシステム時刻である場合があります)。

交換する

std::default_random_engine generator;

沿って

std::default_random_engine generator(<some seed number>);

-3

2つの一般的な状況があります。1つ目は、乱数が必要であり、品質や実行速度にそれほど煩わされないことです。その場合、次のマクロを使用します

#define uniform() (rand()/(RAND_MAX + 1.0))

これにより、pが0から1の範囲になります-イプシロン(RAND_MAXがdoubleの精度よりも大きい場合を除きますが、その場合は心配する必要があります)。

int x =(int)(uniform()* N);

0からN -1までのランダムな整数を指定します。

他の分布が必要な場合は、pを変換する必要があります。あるいは、uniform()を数回呼び出す方が簡単な場合もあります。

反復可能な動作が必要な場合は、定数でシードします。それ以外の場合は、time()の呼び出しでシードします。

品質や実行時のパフォーマンスが気になる場合は、uniform()を書き直してください。しかし、それ以外の場合はコードに触れないでください。0から1マイナスイプシロンで、常にuniform()を維持します。C ++の乱数ライブラリをラップして、より優れたuniform()を作成できるようになりましたが、これは中程度のオプションです。RNGの特性について気になっている場合は、基礎となるメソッドがどのように機能するかを理解するために少し時間を費やして、それを提供することも価値があります。したがって、コードを完全に制御でき、同じシードを使用すると、プラットフォームやリンクしているC ++のバージョンに関係なく、シーケンスが常にまったく同じになることが保証されます。


3
ただし、均一ではありません(0からN-1)。理由は簡単です。N= 100でRAND_MAX = 32758だとしましょう。32758要素(RAND_MAX)を100入力に均一にマッピングする方法はありません。独自の方法は32000に境界を設定し、境界を超えた場合にrand()を再実行します
amchacon

1
Nが100の場合、RNGはフラット分布からの偏差を検出できるように非常に優れている必要があります。
Malcolm McLean
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.