본문 바로가기

Programming/CPP11&14

[C++11] 가중치를 적용해서 랜덤 넘버(Random Number) 생성

반응형

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만 번 돌렸을 때 원하는 수치에 근접합니다.

이산 분포를 적용해서 쉽게 가중치가 적용된 랜덤 넘버를 활용할 수 있습니다.

반응형