[C++] 난수 생성

chooha·2025년 1월 7일

C++

목록 보기
4/23

1. rand()

#include <iostream>
#include <stdlib.h>
#include <time.h>

int main() {
  srand(time(NULL));

  for (int i = 0; i < 5; i++) {
    std::cout << "난수 : " << rand() % 100 << '\n';
  }
  return 0;
}

0~99까지의 난수를 생성하는 코드
엄밀히 말하자면 마치 난수처럼 보이는 의사 난수를 생성하는 코드임
무작위로 정해진 첫 번째 수를 시드(seed)라고 부르는데, C의 경우 srand를 통해 seed를 설정할 수 있음
여기서는 time(NULL)을 통해 프로그램을 실행했던 초를 시드값으로 지정했음
그렇게 되면 rand()는 호출할 때마다 시드값을 기반으로 무작위처럼 보이는 수열을 생성하게 됨
위와 같은 방식의 문제는

  • 시드값이 너무 천천히 변함
  • 0부터 99까지 균등하게 난수를 생성하지 않음
    그냥 rand()를 썼을때는 리턴하는 값이 0부터 RAND_MAX이기 때문에 모든 값들이 같은 확률이지만 %100을 하게 되면 숫자마다 확률이 달라지게 됨
    • EX. RAND_MAX가 128일 때,
      1의 경우 rand()가 리턴한 값이 1이거나 101일 때 생성되지만 50의 경우 rand()가 리턴한 값이 50일 때만 생성됨
      ∴ 1이 뽑힐 확률이 50이 뽑힐 확률보다 2배나 높음
  • rand() 자체도 별로 뛰어나지 않음
    C의 rand()함수는 선형 합동 생성기(Linear congruential generator)라는 알고리즘을 바탕으로 구현되어 있는데 이 알고리즘은 썩 좋은 품질의 난수열을 생성하지 못함

2. <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;
  }
}
  • random_device
std::random_device rd;

수학적 알고리즘을 통해 생성되는 가짜 난수가 아닌 컴퓨터가 실행하면서 마주치는 무작위적인 요소들(ex. 장치드라이버들의 noise)을 기반으로한 진정한 난수 제공
다만, 컴퓨터가 주변의 환경과 무작위적으로 상호작용하면서 만들어지는 것이기 때문에 의사 난수보다 난수를 생성하는 속도가 매우 느림
따라서, 시드값처럼 난수 엔진을 초기화하는데 사용하고, 그 이후의 난수열은 난수 엔진으로 생성하는 것이 적합함

  • mt19937
    c++ <random> 라이브러리에서 제공하는 난수 생성 엔진 중 하나로, 메르센 트위스터라는 알고리즘을 사용함
    생성되는 난수들 간의 상관관계가 매우 작기 때문에 여러 시뮬레이션에서 사용할 수 있음
    하지만, 객체 크기가 커서(2KB 이상) 메모리가 부족한 시스템에서는 적합하지 않을 수 있음

    💡 <random> 라이브러리의 minstd_rand
    : rand와 같이 선형 합동 알고리즘을 사용한 엔진

std::mt19937 gen(rd());

생성한 random_device 객체를 이용해서 난수 생성 엔진 객체를 정의
만약 random_device대신에 그냥 원하는 값을 시드값으로 넣어주고 싶다면

std::mt19937 gen(1234);

이런식으로 값을 넣어주면 됨

  • uniform_int_distribution<int>
std::uniform_int_distribution<int> dis(0,99);

균등한 확률로 정수를 뽑아내고 싶다면 위와 같이 균등 분포 객체를 정의해야 함
위와 같이 생성자에 원하는 범위를 써넣으면 됨

for (int i = 0; i < 5; i++) {
    std::cout << "난수 : " << dis(gen) << std::endl;
  }

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

💡 normal_distribution
: <random> 라이브러리에 있는 여러가지 분포들 중 하나인 정규 분포

std::normal_distribution<double> dist(0, 1);

위의 코드는 평균 0이고 표준 편차가 1인 정규 분포를 정의한 것


< 참고 자료 >

<random> 라이브러리를 활용한 난수 생성

0개의 댓글