본문 바로가기
개발/C++ (98,03,11,14,17,20,23)

Modern C++ : std::mt19937 (11, 14, 17, 20)

by snowoods 2025. 9. 4.

Modern C++

랜덤 숫자 생성 (Random Number Generation)

 

개요

C++에서의 랜덤 숫자 생성은 <random> 헤더를 통해 제공되는 다양한 유틸리티를 사용하여 구현할 수 있습니다. C++11부터 도입된 이 라이브러리는 예측 가능한 의사 난수 생성기와 다양한 분포를 제공합니다.

 

C++ 버전별 주요 키워드 도입 시기

  • C++11: <random> 헤더, std::random_device, std::mt19937, std::uniform_int_distribution 등 도입
  • C++14: std::make_unique를 사용한 랜덤 엔진 생성이 용이해짐
  • C++17: std::sample 알고리즘 추가
  • C++20: std::uniform_random_bit_generator 개념(concept) 도입

 

내용 설명

C++에서 랜덤 숫자 생성을 위해서는 다음과 같은 컴포넌트들이 사용됩니다:

  1. 시드 생성기 (Seed Generator):
    • std::random_device: 하드웨어 기반의 난수 생성기로, 시드 생성에 주로 사용됩니다.
    • 시드가 예측 불가능해야 할 경우에만 std::random_device를 사용하고, 그렇지 않다면 상수 값을 사용할 수도 있습니다.
  2. 의사 난수 엔진 (Pseudo-random Number Engine):
    • std::mt19937: 메르센 트위스터 알고리즘을 구현한 32비트 의사 난수 생성기
    • std::mt19937_64: 64비트 버전의 메르센 트위스터
    • std::minstd_rand: 더 간단하고 빠른 선형 합동 생성기
  3. 분포 (Distribution):
    • std::uniform_int_distribution: 균일한 정수 분포
    • std::uniform_real_distribution: 균일한 실수 분포
    • std::normal_distribution: 정규 분포
    • std::bernoulli_distribution: 베르누이 분포
    • 기타 다양한 통계적 분포 제공

 

예제 코드

#include <cstdint>
#include <iostream>
#include <random>
#include <vector>

namespace
{
constexpr auto NUM_ELEMENTS = size_t{1'000'000U};
};

int main()
{
    // 1. 벡터 생성 및 0으로 초기화
    auto my_vector = std::vector<std::int32_t>(NUM_ELEMENTS, 0U);

    // 2. 난수 생성기 초기화
    auto seed = std::random_device{};  // 시드 생성기
    auto gen = std::mt19937{seed()};   // 메르센 트위스터 엔진

    // 3. 분포 설정 (-10에서 10 사이의 균일한 정수 분포)
    auto dist = std::uniform_int_distribution<std::int32_t>{-10, 10};

    // 4. 벡터에 난수 채우기
    for (auto &val : my_vector)
    {
        val = dist(gen);  // 분포를 사용하여 난수 생성
    }

    // 5. 처음 10개 요소 출력
    for (std::size_t i = 0; i < 10; ++i)
    {
        std::cout << my_vector[i] << '\n';
    }

    return 0;
}

 

실행 결과

프로그램을 실행할 때마다 다른 난수들이 생성됩니다. 예시 출력:

3
-5
7
2
-8
4
1
-2
9
0

 

활용팁

  1. 성능: std::random_device는 암호학적으로 안전하지만 상대적으로 느릴 수 있습니다. 성능이 중요한 경우, 프로그램 시작 시 한 번만 시드를 생성하고 재사용하세요.
  2. 디버깅: 재현 가능한 결과가 필요하다면, 시드를 고정값으로 설정할 수 있습니다.
  3. // 디버깅용 고정 시드 std::mt19937 gen(42); // 고정된 시드 값
  4. 스레드 안전: 난수 생성기는 스레드 안전하지 않습니다. 각 스레드마다 별도의 생성기 인스턴스를 사용하거나, 뮤텍스로 보호해야 합니다.
  5. 분포 선택: 필요한 통계적 특성에 맞는 적절한 분포를 선택하세요. 예를 들어, 정규 분포가 필요한 경우 std::normal_distribution을 사용할 수 있습니다.
  6. C++17의 std::sample: 컨테이너에서 무작위 표본을 추출할 때 유용합니다.
  7. std::vector<int> source = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; std::vector<int> out; std::sample(source.begin(), source.end(), std::back_inserter(out), 3, std::mt19937{std::random_device{}()});