C++ 난수 생성 | "random" 라이브러리 가이드
이 글의 핵심
C++ 난수 생성에 대한 실전 가이드입니다. 개념부터 실무 활용까지 예제와 함께 상세히 설명합니다.
rand() 문제점
// ❌ 구식 방법
srand(time(0));
int x = rand() % 100; // 0-99
// 문제점:
// 1. 균등 분포 아님
// 2. 품질 낮음
// 3. 스레드 안전 아님
현대적 난수 (C++11)
#include <random>
int main() {
// 시드
random_device rd;
// 난수 엔진
mt19937 gen(rd());
// 분포
uniform_int_distribution<> dis(1, 100);
// 난수 생성
for (int i = 0; i < 10; i++) {
cout << dis(gen) << " ";
}
}
난수 엔진
// Mersenne Twister (권장)
mt19937 gen32; // 32비트
mt19937_64 gen64; // 64비트
// 선형 합동 생성기 (빠름, 품질 낮음)
minstd_rand gen;
// 빼기 합동 생성기
ranlux24 gen;
분포
균등 분포
// 정수
uniform_int_distribution<> intDis(1, 6); // 주사위
int dice = intDis(gen);
// 실수
uniform_real_distribution<> realDis(0.0, 1.0);
double x = realDis(gen);
정규 분포
normal_distribution<> normalDis(100.0, 15.0); // 평균 100, 표준편차 15
double iq = normalDis(gen);
기타 분포
// 베르누이 (참/거짓)
bernoulli_distribution coinFlip(0.5); // 50%
bool result = coinFlip(gen);
// 이항 분포
binomial_distribution<> binDis(10, 0.5);
int heads = binDis(gen);
// 포아송 분포
poisson_distribution<> poisDis(4.0);
int events = poisDis(gen);
// 지수 분포
exponential_distribution<> expDis(1.0);
double time = expDis(gen);
실전 예시
예시 1: 주사위 시뮬레이션
#include <random>
#include <map>
int main() {
random_device rd;
mt19937 gen(rd());
uniform_int_distribution<> dice(1, 6);
map<int, int> histogram;
// 10000번 굴리기
for (int i = 0; i < 10000; i++) {
int roll = dice(gen);
histogram[roll]++;
}
// 결과 출력
for (const auto& [value, count] : histogram) {
cout << value << ": " << string(count / 100, '*') << endl;
}
}
예시 2: 랜덤 문자열
string generateRandomString(size_t length) {
static random_device rd;
static mt19937 gen(rd());
static uniform_int_distribution<> dis(0, 61);
const string chars =
"0123456789"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
string result;
for (size_t i = 0; i < length; i++) {
result += chars[dis(gen)];
}
return result;
}
int main() {
cout << generateRandomString(10) << endl;
cout << generateRandomString(20) << endl;
}
예시 3: 셔플
#include <algorithm>
int main() {
vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
random_device rd;
mt19937 gen(rd());
shuffle(v.begin(), v.end(), gen);
for (int x : v) {
cout << x << " ";
}
}
예시 4: 가중치 랜덤
#include <random>
int main() {
random_device rd;
mt19937 gen(rd());
// 가중치: 10%, 30%, 60%
discrete_distribution<> dis({10, 30, 60});
map<int, int> histogram;
for (int i = 0; i < 10000; i++) {
int choice = dis(gen);
histogram[choice]++;
}
for (const auto& [choice, count] : histogram) {
cout << "선택 " << choice << ": " << count << "회" << endl;
}
}
시드 설정
// 시간 기반 (재현 불가)
mt19937 gen1(time(0));
// random_device (권장)
random_device rd;
mt19937 gen2(rd());
// 고정 시드 (재현 가능)
mt19937 gen3(12345);
// 시드 시퀀스
seed_seq seq{1, 2, 3, 4, 5};
mt19937 gen4(seq);
자주 발생하는 문제
문제 1: 매번 엔진 생성
// ❌ 비효율
int getRandom() {
random_device rd;
mt19937 gen(rd()); // 매번 생성 (느림)
uniform_int_distribution<> dis(1, 100);
return dis(gen);
}
// ✅ static 사용
int getRandom() {
static random_device rd;
static mt19937 gen(rd());
static uniform_int_distribution<> dis(1, 100);
return dis(gen);
}
문제 2: 시드 재사용
// ❌ 같은 시드
for (int i = 0; i < 10; i++) {
mt19937 gen(12345); // 항상 같은 시퀀스
cout << gen() << endl;
}
// ✅ 엔진 재사용
mt19937 gen(12345);
for (int i = 0; i < 10; i++) {
cout << gen() << endl;
}
문제 3: 범위 편향
// ❌ 편향됨
int x = rand() % 100; // 균등하지 않음
// ✅ 균등 분포
uniform_int_distribution<> dis(0, 99);
int x = dis(gen);
성능 비교
#include <chrono>
int main() {
const int N = 10000000;
// rand()
srand(time(0));
auto start = chrono::high_resolution_clock::now();
for (int i = 0; i < N; i++) {
int x = rand();
}
auto end = chrono::high_resolution_clock::now();
cout << "rand(): " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "ms" << endl;
// mt19937
random_device rd;
mt19937 gen(rd());
start = chrono::high_resolution_clock::now();
for (int i = 0; i < N; i++) {
int x = gen();
}
end = chrono::high_resolution_clock::now();
cout << "mt19937: " << chrono::duration_cast<chrono::milliseconds>(end - start).count() << "ms" << endl;
}
FAQ
Q1: rand() vs random?
A:
- rand(): 구식, 품질 낮음
- random: 현대적, 품질 높음, 유연함
Q2: random_device는 항상 사용해야 하나요?
A: 시드로만 사용하세요. 난수 생성은 엔진을 사용하세요.
Q3: 어떤 엔진을 사용하나요?
A: 대부분 mt19937이 적합합니다.
Q4: 재현 가능한 난수는?
A: 고정 시드를 사용하세요.
Q5: 스레드 안전한가요?
A: 엔진과 분포를 스레드별로 생성하세요.
Q6: Random 학습 리소스는?
A:
- cppreference.com
- “The C++ Standard Library” (Nicolai Josuttis)
- “Effective Modern C++“
같이 보면 좋은 글 (내부 링크)
이 주제와 연결되는 다른 글입니다.
- C++ Random | “난수 생성” 가이드
- C++ Distribution | “확률 분포” 가이드
- C++ random_device | “하드웨어 난수” 가이드
관련 글
- C++ Distribution |
- C++ random_device |
- C++ Random |
- C++ async & launch |
- C++ Atomic Operations |