C++11에 추가된 Random Number 생성은 좀 더 정교한 난수 생성이 가능합니다.
기본적인 난수 생성에 관한 내용은 아래 링크에서 확인 가능합니다.
2015/05/22 - [Programming/CPP11&14] - [C++11] 새로워진 random number 생성
랜덤한 번호를 생성할 때 각 값마다 가중치를 주고 싶은 경우가 있습니다.
경품을 추첨할 때 각 등수마다 다른 확률로 선별하고 싶을 때 사용 가능합니다.
가중치가 적용된 랜덤은 이산 분포(Discrete Distribution)를 활용해서 쉽게 구현이 가능합니다.
생성 코드는 다음과 같습니다.
#include <random> #include <vector> template <typename T> int MakeRandNum(std::vector<T> vecProb) { std::random_device rd; std::mt19937 gen(rd()); std::discrete_distribution<T> d{ vecProb.cbegin(), vecProb.cend() }; /* auto it = vecProb.cbegin(); std::discrete_distribution<> d(vecProb.size(), 0, 0, [&](T x)->T { auto val = *it; it++; return val; }); */ return d(gen); }
먼저 std::random_device를 통해 제너레이터를 생성합니다.
디바이스 기반으로 생성하기 때문에 여러 번 호출하는 경우에도 가중치가 적용됩니다.
일반 랜덤값 생성을 할 때는 std::uniform_int_distribution을 사용했습니다.
가중치를 주기 위해서는 std::discrete_distribution을 사용합니다.
초기화는 Initializer list를 통해서 간단하게 초기화가 가능합니다.
Initializer list는 아래 링크에서 확인 가능합니다.
2016/12/25 - [Programming/CPP11&14] - [C++11] 초기화자 리스트(initializer list)와 std::initializer_list
Initializer list가 적용되지 않는 컴파일러에서는 번거롭지만 주석 부분의 방식을 사용해서 초기화합니다.
MakeRandNum()을 사용하는 방법입니다.
#include <map> #include <iostream> #include <random> #include <vector> template <typename T> int MakeRandNum(std::vector<T> vecProb) { std::random_device rd; std::mt19937 gen(rd()); std::discrete_distribution<T> d{ vecProb.cbegin(), vecProb.cend() }; return d(gen); } int main() { std::vector<int> probList; probList.push_back(5); probList.push_back(10); probList.push_back(35); probList.push_back(50); std::map<int, int> probMap; for (int i = 0 ; i < 100000 ; ++i) { ++probMap[MakeRandNum(probList)]; } for (auto item : probMap) { std::cout << item.first << " was made " << item.second << " times" << std::endl; } return 0; }
probList를 만들어서 각 값의 가중치를 넣었습니다.
push_back()으로 입력된 모든 값의 합(5 + 10 + 35 + 50 = 100)이 전체 확률이 됩니다.
그리고 각 값(5, 10, 35, 50)이 0~3까지의 확률이 됩니다.
랜덤값은 0번부터 생성이 되는데 여기서는 4개의 값이 들어갔기 때문에 0~3사이의 값이 나오게 됩니다.
값이 정상적인 분포로 생성되었는지 확인하기 위해 std::map을 사용해서 생성될 때마다 횟수를 추가합니다.
확률을 0은 5%(5/100), 1은 10%(10/100)로 설정했는데 대략 10만 번 돌렸을 때 원하는 수치에 근접합니다.
이산 분포를 적용해서 쉽게 가중치가 적용된 랜덤 넘버를 활용할 수 있습니다.
'Programming > CPP11&14' 카테고리의 다른 글
[C++11] 배열을 편리하게 사용할 수 있는 std::array (0) | 2017.08.26 |
---|---|
[C++11] 초기화자 리스트(initializer list)와 std::initializer_list (0) | 2016.12.25 |
[C++11] Storage class specifiers에 thread_local 추가 (0) | 2015.07.07 |
[C++11] std::mutex를 통한 thread 동기화 (0) | 2015.06.29 |
[C++11] thread 지원 - future를 통한 return값 획득 (2) (0) | 2015.06.28 |