std::default_random_engine을 선언해놓고
std::uniform_int_distribution 객체를 생성해 operator()의 인자로 주는 기존 c++스타일 랜덤은 몇 가지 한계가 있다
- 가장 중요한 이유로, std::default_random_engine은 용량이 커 전역 객체인 편이 나은데, 이 전역 객체는 코드를 유지보수하기 어렵게 만든다. 전역 객체는 어디에서나 접근 가능하기 때문에 오류나 수정 사항이 발생하면 모든 코드를 검토해야 하기 때문이다
- 심지어는, 여러 개의 헤더 파일로 이루어진 코드에서 extern 키워드를 통해 전역 객체에 접근하는 경우, 링크 순서가 꼬이면 도무지 이해 할 수 없는 디버그 메시지를 내뱉는 링크 에러의 지옥에 빠지게 된다
- 사용자가 객체의 이름을 마음대로 정할 수 있기 때문에 객체의 이름을 항상 외우고 있어야 하며, 코드의 통일성이 부족해진다
- 한 번에 여러 개의 랜덤값이 필요한 경우, 반복문의 사용이 필수적이다
개선된 랜덤 함수를 통해 조금 더 코드를 작성하기 쉬워진다
- const int random_value(const std::uniform_int_distribution<>& uid)
설명 : std::uniform_int_distribution 분포의 랜덤값을 한 개 생성하여 반환한다
예시- 0, 100 사이의 난수 생성
std::uniform_int_distribution uid{ 0, 100 };
int n = random_value(uid); - 50, 200 사이의 난수 생성
std::uniform_int_distribution<> uid2{ 50, 200 };
int m = random_value(uid2);
- 0, 100 사이의 난수 생성
- const int random_value(const int lower_bound, const int upper_bound)
설명 : [lower_bound, upper_bound] 범위의 랜덤값을 한 개 생성하여 반환한다
예시- 0, 100 사이의 난수 생성
int n = random_value(0, 100); - 50, 200 사이의 난수 생성
int m = random_value(50, 200);
- 0, 100 사이의 난수 생성
- std::vector<int> random_value_vector(const std::uniform_int_distribution<>& uid, const size_t cnt)
설명 : std::uniform_int_distribution 분포의 랜덤값을 cnt 개 만큼 생성하여 벡터로 반환한다
예시- 0, 100 사이의 난수 10개 생성
std::uniform_int_distribution<> uid{ 0, 100 };
std::vector<int> v{ random_value_vector(uid, 10) }; - 50, 200 사이의 난수 30개 생성
std::uniform_int_distribution<> uid2{ 50, 200 };
std::vector<int> nums{ random_value_vector(uid2, 30) };
- 0, 100 사이의 난수 10개 생성
- std::vector<int> random_value_vector(const int lower_bound, const int upper_bound, const size_t cnt)
설명 : [lower_bound, upper_bound] 범위의 랜덤값을 cnt 개 만큼 생성하여 벡터로 반환한다
예시- 0, 100 사이의 난수 10개 생성
std::vector<int> v{ random_value_vector(0, 100, 10) }; - 50, 200 사이의 난수 30개 생성
std::vector<int> nums{ random_value_vector(50, 200, 30) };
- 0, 100 사이의 난수 10개 생성
- template <size_t N>
std::array<int, N> random_value_array(const std::uniform_int_distribution<>& uid)
설명 : std::uniform_int_distribution 분포의 랜덤값을 N 개 만큼 생성하여 배열로 반환한다
예시- 0, 100 사이의 난수 10개 생성
std::uniform_int_distribution<> uid{ 0, 100 };
std::array<int, 10> arr{ random_value_array<10>(uid) }; - 50, 200 사이의 난수 30개 생성
std::uniform_int_distribution<> uid2{ 50, 200 };
std::array<int, 30> nums{ random_value_array<30>(uid2) };
- 0, 100 사이의 난수 10개 생성
- template <size_t N>
std::array<int, N> random_value_array(const int lower_bound, const int upper_bound)
설명 : [lower_bound, upper_bound] 범위의 랜덤값을 N 개 만큼 생성하여 배열로 반환한다
예시- 0, 100 사이의 난수 10개 생성
std::array<int, 10> arr{ random_value_array<10>(0, 100) }; - 50, 200 사이의 난수 30개 생성
std::array<int, 30> nums{ random_value_array<30>(50, 200) };
- 0, 100 사이의 난수 10개 생성
random_value와 random_value_vector 함수는 double 버전으로도 오버로딩하였다
// randomvalue.h
#ifndef _randomvalue
#define _randomvalue
#include <random>
#include <vector>
namespace _rv
{
std::random_device rd;
std::default_random_engine dre(rd());
}
// ...
// ...
|
cs |
사용자는 더이상 std::default_random_engine을 생성할 필요가 없다
random_value와 random_value_vector는 절대로 std::default_random_engine을 사용자에게 직접 요구하지 않는다!
- std::default_random_engine my_random_engine;
std::uniform_int_distribution<> uid{ 0, 1 };
int coin = uid(my_random_engine); - std::uniform_int_distribution<> uid{ 0, 100 };
int n = uid(_rv::dre);
random_value 함수로 랜덤값을 생성하기로 정했다면, 위와 같은 코드가 나올 수조차 없다
다만 사용자가 굳이 std::default_random_engine을 생성하거나, 한 발 더 가서 숨겨둔 네임스페이스에서 dre를 꺼내오는 비상식적인 행동을 하더라도 말리진 않는다
제대로 쓰긴 쉽지만, 틀리게 쓰긴 어렵게 만들어져있을 뿐이다
// randomvalue.h
const auto random_value(const std::uniform_int_distribution<>& uid)
{
return uid(_rv::dre);
}
const auto random_value(const std::uniform_real_distribution<>& urd)
{
return urd(_rv::dre);
}
|
cs |
랜덤 범위를 여러 곳에서 이용하는 경우 std::uniform_(...)_distribution을 생성해 std::uniform_(...)_distribution을 매개변수로 취하는 random_value를 호출하여 랜덤값을 얻는다
예시
- std::uniform_int_distribution<> uid{ 80, 160 };
int n = random_value(uid);
int m = random_value(uid); - std::uniform_real_distribution<> urd{ 80.0, 160.0 };
double lf = random_value(urd);
double score = random_value(urd);
// randomvalue.h
const auto random_value(const int lower_bound, const int upper_bound)
{
return random_value(std::uniform_int_distribution<>{lower_bound, upper_bound});
}
const auto random_value(const double lower_bound, const double upper_bound)
{
return random_value(std::uniform_real_distribution<>{lower_bound, upper_bound});
}
|
cs |
랜덤 범위가 일회성일 때에는 lower_bound, upper_bound 를 매개변수로 취하는 random_value를 호출하여 랜덤값을 얻는다
이때, std::uniform_(...)_distribution을 생성하는 과정이 생략되므로 코드는 한 줄이 된다
예시
- int n = random_value(1000, 2000);
- double lf = random_value(1.234, 5.678);
// randomvalue.h
auto random_value_vector(const std::uniform_int_distribution<>& uid, const size_t cnt)
{
std::vector<int> v;
v.reserve(cnt);
for (int i = 0; i < cnt; ++i)
v.push_back(random_value(uid));
return v;
}
auto random_value_vector(const std::uniform_real_distribution<>& urd, const size_t cnt)
{
std::vector<double> v;
v.reserve(cnt);
for (int i = 0; i < cnt; ++i)
v.push_back(random_value(urd));
return v;
}
|
cs |
한 번에 여러 개의 랜덤 값을 동시에 얻고 싶을 경우 random_value_vector 함수를 호출한다
지정된 범위에서 cnt 개 만큼의 랜덤값을 생성하여 벡터로 반환한다
랜덤 범위를 여러 곳에서 이용하는 경우 std::uniform_(...)_distribution을 생성해 std::uniform_(...)_distribution을 매개변수로 취하는 random_value_vector를 호출하여 랜덤값을 얻는다
예시
- std::uniform_int_distribution<> uid{ 555, 666 };
auto v_25{ random_value_vector(uid, 25) };
auto v_33{ random_value_vector(uid, 33) }; - std::uniform_real_distribution<> urd{ 200.0, 400.55 };
auto real_numbers{ random_value_vector(urd, 100) };
auto tree_heights{ random_value_vector(urd, 50) };
자료구조를 통으로 반환하므로 랜덤값 여러개를 한번에 저장해놓고 이후에 수정할 수 있으며
벡터의 메서드인 push_back()이나 emplace_back(), insert(), emplace()를 이용하여 값을 추가할 수 있다
// randomvalue.h
auto random_value_vector(const int lower_bound, const int upper_bound, const size_t cnt)
{
return random_value_vector(std::uniform_int_distribution<>{lower_bound, upper_bound}, cnt);
}
auto random_value_vector(const double lower_bound, const double upper_bound, const size_t cnt)
{
return random_value_vector(std::uniform_real_distribution<>{lower_bound, upper_bound}, cnt);
}
|
cs |
역시 한 번에 여러 개의 랜덤 값을 동시에 얻고 싶을 경우 random_value_vector 함수를 호출한다
지정된 범위에서 cnt 개 만큼의 랜덤값을 생성하여 벡터로 반환한다
랜덤 범위가 일회성일 때에는 lower_bound와 upper_bound를 매개변수로 취하는 random_value_vector를 호출하여 랜덤값들을 얻는다
std::uniform_(...)_distribution을 생성하는 과정이 생략되므로 코드는 한 줄이 된다
예시
- auto vector1{ random_value_vector(10, 30, 50) };
- auto vector2{ random_value_vector(2.3, 4.5, 6) };
// randomvalue.h
template <size_t N>
auto random_value_array(const std::uniform_int_distribution<>& uid)
{
std::array<int, N> a;
for (auto& elem : a)
elem = random_value(uid);
return a;
}
template <size_t N>
auto random_value_array(const std::uniform_real_distribution<>& urd)
{
std::array<double, N> a;
for (auto& elem : a)
elem = random_value(urd);
return a;
}
|
cs |
한 번에 여러 개의 랜덤 값을 동시에 얻고 싶을 경우 random_value_array 함수를 호출한다
지정된 범위에서 N 개 만큼의 랜덤값을 생성하여 배열로 반환한다
랜덤 범위를 여러 곳에서 이용하는 경우 std::uniform_(...)_distribution을 생성해 std::uniform_(...)_distribution을 매개변수로 취하는 random_value_array를 호출하여 랜덤값을 얻는다
예시
- std::uniform_int_distribution<> uid{ 1, 9 };
auto grades{ random_value_array<6>(uid) };
auto nums{ random_value_array<2000>(uid) }; - std::uniform_real_distribution<> urd{ 130.0, 230.0 };
auto men_hegihts{ random_value_array<10000>(urd) };
auto women_heights{ random_value_array<5000>(urd) };
random_value_vector를 호출하기에는 빈번한 호출 문제로 동적 할당이 부담스러울 때 random_value_array를 이용한다
템플릿 매개변수 N으로 얻어올 랜덤값의 갯수를 정한다
random_value_array<1>과 random_value_array<2>, random_value_array<3>은 모두 다른 함수이므로
랜덤값의 갯수를 꽤나 동적으로 받아야 할 경우엔 되도록 array 버전 대신 vector 버전을 사용한다
1~100000 까지의 숫자를 전부 이용한다면 100000개의 서로 다른 함수가 컴파일러에 의해 작성되어 코드 비대화(code bloating)가 발생한다
// randomvalue.h
template <size_t N>
auto random_value_array(const int lower_bound, const int upper_bound)
{
return random_value_array<N>(std::uniform_int_distribution<>{lower_bound, upper_bound});
}
template <size_t N>
auto random_value_array(const double lower_bound, const double upper_bound)
{
return random_value_array<N>(std::uniform_real_distribution<>{lower_bound, upper_bound});
}
|
cs |
역시 한 번에 여러 개의 랜덤 값을 동시에 얻고 싶을 경우 random_value_array 함수를 호출한다
지정된 범위에서 N 개 만큼의 랜덤값을 생성하여 배열로 반환한다
랜덤 범위가 일회성일 때에는 lower_bound와 upper_bound를 매개변수로 취하는 random_value_array를 호출하여 랜덤값들을 얻는다
std::uniform_(...)_distribution을 생성하는 과정이 생략되므로 코드는 한 줄이 된다
예시
- auto arr1{ random_value_array<60>(20, 40) };
- auto arr2{ random_value_array<100>(13.57, 35.79) };
모든 함수들이 내부적으로는 std::default_random_engine과 std::uniform_(...)_distribution 둘 다 사용한다
그러나 std::default_random_engine은 사용자가 사용하지 않는 네임스페이스 _rv로 옮겨갔으며
uniform_(...)_distribution도 굳이 사용자가 생성할 필요가 없다면 생성하지 않을 수 있다
// randomvalue.h
#ifndef _randomvalue
#define _randomvalue
#include <random>
#include <vector>
#include <array>
namespace _rv
{
std::random_device rd;
std::default_random_engine dre(rd());
}
const auto random_value(const std::uniform_int_distribution<>& uid)
{
return uid(_rv::dre);
}
const auto random_value(const int lower_bound, const int upper_bound)
{
return random_value(std::uniform_int_distribution<>{lower_bound, upper_bound});
}
auto random_value_vector(const std::uniform_int_distribution<>& uid, const size_t cnt)
{
std::vector<int> v;
v.reserve(cnt);
for (int i = 0; i < cnt; ++i)
v.push_back(random_value(uid));
return v;
}
auto random_value_vector(const int lower_bound, const int upper_bound, const size_t cnt)
{
return random_value_vector(std::uniform_int_distribution<>{lower_bound, upper_bound}, cnt);
}
template <size_t N>
auto random_value_array(const std::uniform_int_distribution<>& uid)
{
std::array<int, N> a;
for (auto& elem : a)
elem = random_value(uid);
return a;
}
template <size_t N>
auto random_value_array(const int lower_bound, const int upper_bound)
{
return random_value_array<N>(std::uniform_int_distribution<>{lower_bound, upper_bound});
}
const auto random_value(const std::uniform_real_distribution<>& urd)
{
return urd(_rv::dre);
}
const auto random_value(const double lower_bound, const double upper_bound)
{
return random_value(std::uniform_real_distribution<>{lower_bound, upper_bound});
}
auto random_value_vector(const std::uniform_real_distribution<>& urd, const size_t cnt)
{
std::vector<double> v;
v.reserve(cnt);
for (int i = 0; i < cnt; ++i)
v.push_back(random_value(urd));
return v;
}
auto random_value_vector(const double lower_bound, const double upper_bound, const size_t cnt)
{
return random_value_vector(std::uniform_real_distribution<>{lower_bound, upper_bound}, cnt);
}
template <size_t N>
auto random_value_array(const std::uniform_real_distribution<>& urd)
{
std::array<double, N> a;
for (auto& elem : a)
elem = random_value(urd);
return a;
}
template <size_t N>
auto random_value_array(const double lower_bound, const double upper_bound)
{
return random_value_array<N>(std::uniform_real_distribution<>{lower_bound, upper_bound});
}
// ...
// ...
#endif
|
cs |
// randomvalue.h
// ...
#include <iostream>
void test_random_value()
{
for (int i = 0; i < 10; ++i)
{
auto lower_bound{ random_value(0, 10000) };
auto upper_bound{ random_value(lower_bound, 10000) };
auto n = random_value_vector(lower_bound, upper_bound, 5);
auto random_num = random_value(std::uniform_int_distribution<>{lower_bound, upper_bound});
std::uniform_int_distribution<> uid{ lower_bound, upper_bound };
auto random_num2 = random_value(uid);
std::cout << "lower_bound : " << lower_bound << " upper_bound : " << upper_bound << std::endl;
std::cout << "{ " << n[0] << ", " << n[1] << ", " << n[2] << ", " << n[3] << ", " << n[4] << ", " << random_num << ", " << random_num2 << " }\n\n";
}
}
// ...
|
cs |
테스트 결과
벡터, int 버전
벡터, double 버전
배열, int 버전
배열, double 버전
모두 테스트를 통과하였다
'Project > Win32API로 던그리드 모작' 카테고리의 다른 글
빌더 패턴 - 직사각형 만들기 (0) | 2021.08.18 |
---|---|
* passkey 패턴 : public 멤버 함수의 접근 권한 관리로 public 함수를 non-public 하게 (0) | 2021.08.18 |
더블버퍼링 (0) | 2021.08.14 |
싱글톤 패턴 (0) | 2021.08.14 |
좌표 변환 (0) | 2021.08.14 |