Computing

[C++] 진짜 랜덤 숫자 생성하기 (std::random_device) 본문

Programming/C++

[C++] 진짜 랜덤 숫자 생성하기 (std::random_device)

jhson989 2024. 4. 7. 14:15

기존 C-style 랜덤 숫자 생성 방법 및 한계

밑의 코드 예시는 기존 C-style code에서 랜덤 숫자 (정확히는 랜덤한 것처럼 느껴지는 pseudo-random 숫자를 생성) 를 생성하는 코드이다. std::rand()함수[1]는 Seed 숫자를 이용해 어떤 숫자를 생성하는데, std::srand() 함수를 이용하면 std::rand()함수가 사용하는 Seed 숫자를 설정할 수 있다. 밑의 코드에서는 std::srand() 함수를 이용해 Seed를 현재 시간으로 설정하였다.
 

#include <ctime>
#include <cstdlib>
...

    std::srand(time(NULL)); 랜덤 생성 시의 Seed 설정. Seed를 현재 시간으로 설정한다.
    for (int i=0; i<10; i++) {
    	// int std::rand() 함수는 의사 난수 (pseudo-random, 랜덤한 것같은 숫자) 숫자를 생성함
        // int std::rand() 가 생성하는 의사 난수는 0부터 RAND_MAX(32767) 중 하나이다.
        int random_value = std::rand() % 100; 
    }

... // 계속

 
문제는 C-style std::rand() 함수가 생성하는 랜덤 숫자는 그 퀄리티에 문제가 있다고 한다[1].
1. 기본적으로 랜덤하게 생성되는 숫자의 크기가 최대 32767밖에 안된다.
2. 생성되는 숫자들의 분포가 균일하지 않는다고 한다.
3. 랜덤한 숫자를 생성하는 알고리즘이 존재하기에 std::rand() 함수가 생성하는 의사 난수는 결정적(deterministic)이고 std::srand()에서 설정한 Seed값만 알면 어떤 랜덤한 숫자가 생성되는 지를 알 수 있다.
구체적인 단점은 다음과 같다[2].
 
 
 

std::random_device의 도입과 예시

다양한 시뮬레이션 환경을 개발할 때, 균일한 분포를 가지는 진정으로 랜덤한 숫자에 대한 필요성은 항상 존재할 것이다. Modern C++ (Since C++11) 부터는 std::rand() 함수를 대체하기 위해 std::random_device class[3]가 도입되었다. std::random_device는 균일한 분포(uniformly-distributed)를 가진 비결정적(non-deterministic)인 int 숫자를 생성하는 클래스이다.
 
컴퓨터가 비결정적인 숫자를 생성할 수 있다는 것이 놀라운데, 구현에서 실제 물리 디바이스 (hardware) 와 연관된 어떠한 랜덤한 특징을 이용해 비결정적인 진짜 랜덤한 숫자를 생성해낸다고 한다.

std::random_device를 이용한 랜덤 숫자 생성 예시는 다음과 같다.
 

#include <iostream>
#include <random>

int main(void) {

    std::random_device rd;

    std::cout << "Min Value : " << rd.min() << std::endl;
    std::cout << "Max Value : " << rd.max() << std::endl;

    for (int i=0; i<3; i++) {
        std::cout << " - Generated Random Value : " << rd() << std::endl;
    }

}

////////////////////////
// 결과
// Min Value : 0
// Max Value : 4294967295
//  - Generated Random Value : 3499211612
//  - Generated Random Value : 581869302
//  - Generated Random Value : 389034673

 
std::random_device는 Class이기에 선언을 통해 객체 rd를 초기화한다. rd.min() 함수와 rd.max() 함수를 통해 std::random_device가 생성하는 최소, 최대 값을 확인할 수 있는데, 최소 값은 0u와 같고, 최대 값은 std::numeric_limits<unsigned int>::max() 와 같다.
 
opeator()를 이용하면 드디어 랜덤 숫자를 생성할 수 있다. operator() 실행 시, [min(), max()] 범위에서 균일한 분포를 가지도록 랜덤한 숫자들을 출력한다.

 
 
 

Reference

[1] https://en.cppreference.com/w/cpp/numeric/random/rand

[2] https://stackoverflow.com/questions/53040940/why-is-the-new-random-library-better-than-stdrand
[3] https://en.cppreference.com/w/cpp/numeric/random/random_device