본문 바로가기

Language/C++

[C++] <random> 라이브러리 (난수 생성)

728x90
반응형

C 스타일의 난수 생성의 문제점

아래는 C 스타일로 0부터 99까지의 난수를 생성하는 코드이다.

#include <stdio.h> #include <stdlib.h> #include <time.h> int main(){ srand(time(NULL)); for(int i = 0; i < 5; i++){ print("난수 : %d \n", rand() % 100); } return 0; }

위 코드는 진짜 난수를 생성하는 것이 아니라 마치 난수처럼 보이는 의사 난수(pseudo random number)을 생성하는 코드이다. 첫 번째 수만 무작위로 정하고, 나머지 수들은 그 수를 기반으로 여러가지 수학적 방법을 통해서 난수처럼 보이지만 실제로는 무작위로 생성된 것이 아닌 수열들을 만들어내게 된다.

무작위로 정해진 첫 번째 수를 시드(seed)라고 부르는데, C의 경우 srand를 통해 seed를 설정할 수 있다. 위 프로그램의 경우 time(NULL)을 통해 프로그램을 실행했던 초를 시드값으로 지정하였다. 그리고 rand()는 호출할 때 마다 시드값을 기반으로 무작위처럼 보이는 수열을 생성하게 된다.

하지만 위 코드는 시드값이 너무 천천히 변하고, 균등하게 난수를 생성하지 않는 문제점이 있다. rand() 함수는 일부 시뮬레이션에 사용하기에 적합하지 않다.

C++에서는 C의 srand와 rand를 갖다 버리자!!!

 

<random>

0부터 99까지의 난수를 생성하는 코드를 C++의 <random> 라이브러리를 사용해서 작성해보면

#include <iostream> #include <random> int main(){ // 시드값을 얻기 위한 random_device 생성 std::random_device rd; // random_device를 통해 난수 생성 엔진을 초기화한다. std::mt19937 gen(rd()); // 0부터 99까지 균등하게 나타나는 난수열을 생성하기 위해 균등 분포 정의 std::uniform_int_distribution<int> dis(0, 99); for(int i = 0; i < 5; i++){ std::cout << "난수 : " << dis(gen) << std::endl; } }

C의 경우 time(NULL)을 통해서 시드값을 지정하였지만 이는 여러 가지 문제점이 있었다. random_device를 이용하면 진짜 난수를 이용할 수 있다. 다만 진짜 난수의 경우 컴퓨터가 주변의 환경과 무작위적으로 상호작용하면서 만들어지는 것이기 때문에 의사 난수보다 난수를 생성하는 속도가 매우 느리다. 따라서 시드값처럼 난수 엔진을 초기화 하는 데 사용하고, 이후의 난수열은 난수 엔진으로 생성하는 것이 바람직하다.

std::mt19937는 rand보다 좀 더 양질의 난수열을 생성한다. 무엇보다 생성되는 난수들 간의 상관관계가 매우 작기 때문에 여러 시뮬레이션에서 사용할 수 있다. random_device 객체를 이용해서 난수 생성 엔진을 초기화 하는 대신에 본인이 원하는 시드값으로 넣어주고 싶다면

std::mt19937 gen(1234);

와 같이 해도 된다.

난수 생성 엔진을 만들었지만 바로 난수를 생성할 수 있는 것은 아니다. 어디에서 수를 뽑아낼지 알려주는 분포(distribution)를 정의해야 한다. 0부터 99까지 균등한 확률로 정수를 뽑아내기 위해서 균등분포(Uniform distribution) 객체를 정의해야 한다. uniform_int_distribution<int> 의 생성자에 원하는 범위를 써넣는다.

마지막으로 균등 분포에 사용할 난수 엔진을 전달함으로써 균등 분포에서 무작위로 샘플을 뽑아낼 수 있다.

<random> 라이브러리에서는 균등 분포 말고도 여러가지 분포들을 제공하고 있다. 그 중 가장 많이 쓰이는 정규 분포(Normal distribution)만 살펴보자.

#include <iomanip> 
#include <iostream> 
#include <map> 
#include <random>
int main() { 
    std::random_device rd; 
    std::mt19937 gen(rd()); 
    std::normal_distribution<double> dist(/* 평균 = */ 0, /* 표준 편차 = */ 1); 
    std::map<int, int> hist{}; 
    for (int n = 0; n < 10000; ++n) { 
        ++hist[std::round(dist(gen))]; 
    } 
    for (auto p : hist) { 
        std::cout << std::setw(2) << p.first << ' ' 
            << std::string(p.second / 100, '*') << " " << p.second << '\n'; 
    } 
}

실행 결과

-4 1 
-3 38 
-2 ****** 638 
-1 ************************ 2407 
0 ************************************** 3821 
1 ************************ 2429 
2 ***** 595 
3 70 
4 1
728x90
반응형

'Language > C++' 카테고리의 다른 글

[C++] OOP - this 포인터  (0) 2021.10.24
[C++] OOP - 클래스(class)  (0) 2021.10.24
[C++] <string> 라이브러리  (0) 2021.10.03
[C++] OOP - 생성자와 소멸자  (0) 2021.07.09
[C++] 공용체, 열거체  (0) 2021.07.03