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

4
std::mt19937타당한 이유가없는 한 엔진으로 a 를 사용하는 것이 좋습니다 . 그리고 분포는 양쪽 끝에서 닫힌 간격입니다.
chris


2
@ chris 배포가 양쪽 끝에서 닫히지 않았습니다.이 링크 또는이 링크를
memo1288

1
@ memo1288는, 내가 OP가 사용하던 생각, 감사 std::uniform_int_distribution하는 되어 양쪽에 마감합니다.
chris

답변:


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_device이라는 이름의 난수 생성기를 시드하기 위해 한 번 사용 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

3
당신의 관련없는 @ 크리스 (하지만 당신은 문을 열어) std::vector때문에 비유 여기에 작동하지 않는 std::vector 것입니다 실제로 CPU에 캐싱으로 인해 좋은 기본. std::list중간에 삽입 해도 성능이 뛰어납니다 . 모든 컨테이너를 이해하고 알고리즘 복잡성에 따라 정보에 근거한 결정을 내릴 수있는 경우에도 마찬가지입니다.
void.pointer

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 다른 페이지 ++에 대해, 물건 위의 모든 빨간색 와 감시 "STL"스테판 T. Lavavej에서 비디오를 여전히 내가 알아 내기 위해 전체 일요일을했다, 그래서 확인하는 방법 임의의 숫자 작품 실천에 없었다 그것의 모든 것 그리고 그것이 작동하고 어떻게 사용될 수 있는지.

제 생각에 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 ++ primer 5 판 2012 인용)


마지막으로 Bjarne Stroustrups에서 최신 20 권의 책 중 최고의 설명을 발견했습니다. "C ++ 2019 둘러보기", "C ++ 2016을 사용한 프로그래밍 원칙 및 실습"및 "C ++ 프로그래밍 언어 4 판"에서 그의 내용을 알아야합니다. 2014 "및"Lippmans C ++ primer 5 판 2012 "의 일부 예 :

그리고 난수 생성기는 두 부분으로 구성되어 있기 때문에 정말 간단합니다. (1) 일련의 난수 또는 의사 난수 값을 생성하는 엔진. (2) 해당 값을 범위의 수학적 분포로 매핑하는 분포.


Bjarne Stroustrups는 Microsoft의 STL 직원의 의견에도 불구하고 다음과 같이 씁니다.

에서 표준 라이브러리는 난수 엔진 및 분포 (§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;
    }

나는 그것이 모든 것을 추가하고 내가 말했듯이, 그 예를 들기 위해 많은 독서와 시간이 걸렸다 고 생각합니다-숫자 생성에 대한 더 많은 것들이 있다면 PM을 통해 또는 의견 섹션에서 그것에 대해 들려 드리겠습니다 필요한 경우 추가하거나이 게시물을 수정합니다. 부울


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

~



-3

두 가지 일반적인 상황이 있습니다. 첫 번째는 난수를 원하고 품질이나 실행 속도에 대해 너무 소중하지 않다는 것입니다. 이 경우 다음 매크로를 사용하십시오.

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

RAND_MAX가 double의 정밀도보다 크지 않은 경우 p에서 0-1-epsilon 범위의 p를 제공하지만 올 때 걱정할 필요는 없습니다.

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.