'2013/12'에 해당되는 글 1건

  1. 2013.12.18 C 의 rand() 는 이제 그만합시다.
2013.12.18 18:23

C 의 rand() 는 이제 그만합시다.


게임을 제작할 때 필요한 요소는 뭐니뭐니 해도 랜덤요손데요. 그만큼 게임에서의 랜덤은 중요합니다. 때론 완전 랜덤이 필요하지만, 통제된 랜덤값이 필요한 경우가 있습니다.

예를 들면 전략 시뮬에서의 랜덤성을 생각해보면, 해당 판마다 원하는 시드를 세팅하고 리플레이를 저장할 때 시드를 세팅하고 리플레이를 재생시키면 됩니다.

일반적으로 랜덤값을 구하기 위해서는 rand() 를 쓰게 됩니다. 사실 특별한 목적없이 랜덤이 필요한 경우엔 rand() 를 써도 무관합니다만, 위와 같은 상황에서 rand() 를 쓰게 되면 다음 랜덤값을 보장받지 못하는 상황이 옵니다. 그 이유는 rand() 는 전역함수이기 때문입니다.

뭐? 전역함수인게 뭐 어때서? 라는 생각을 하실지 모르겠는데, 전역함수이기 때문에 우리가 rand() 함수 호출을 제어하지 못합니다. 시드를 세팅하고 rand() 를 돌린다 가정했을 때, 다음 값은 정해진 시드에 영향을 받은 값이 나와야하는데 다른 코드내에서 rand() 가 불렸으면 예측하지 못하게 됩니다. 자기자신은 절대로 그러지 않겠다고 맹세하더라도 남이 짜논 코드에서 rand() 가 불리게 될지 누가 압니까.

 

그리고 rand() 함수의 분포성이 고르지 못하다고 하는데요, 이 사실을 직접 확인해보고 싶어서 표본을 뽑아서 표준편차를 구해봤는데 알려진 좋은 랜덤 생성기와 비교해서 차이는 발견못했습니다. 오차범위정도로 해석할 수 있는 결과가 나왔네요. xcode 5.x 기준 rand() 함수가 성능이 좋나봅니다. 실제로 xcode 내장 rand() 함수는 RAND_MAX 가 0x7fffffff  입니다. 제가 알던 RAND_MAX 는 2^16-1 인데…ㅎㅎ

사실 분포성의 고르지 못하다는 이유보다는 rand() 함수의 스펙이 명시되지 않았다고 보는게 맞습니다. 컴파일러마다 그 구현이 다릅니다.

말로만 하니 좀 답답하실거 같아 분포도를 시각화를 해봤습니다.

iOS Simulator Screen shot 2013

x 축이 나온 숫자이고 y 축이 나온 숫자의 개수입니다.

위가 메르센 트위스터(Mersenne Twister)

아래가 rand()

 

눈으로 보기엔 별 차이 없긴 합니다.

 

이런저런 이유로 좋은 랜덤 객체를 써서 분포도를 고르게 하고 전역성을 제거해야 합니다. C++ 11 표준이 나오기 전에는 각자 랜덤 생성기를 어디서 긁어와서 클래스를 만들고 객체로 관리해야 했지만, C++ 표준위원회는 유명한 랜덤생성기인 메르센 트위스터(Mersenne Twister)를 택하게 됩니다.

메르센 트위스터(이하 MT)는 rand() 의 주기가 2^32 인데 반해, 2^19937 – 1 의 주기를 갖습니다. MT 는 일반적으로 rand() 함수보다 빠르다고 합니다.

게다가 rand() 함수는 고른 랜덤값밖에 생성해내지 못합니다. 정규분포를 따르는 랜덤값을 추출하고 싶을 땐? 베르누이 분포를 따르고 싶을 땐? exponential 분포를 따르고 싶을 땐?

 

rand() 그만하고 C++ 11 random 합시다.
C++ 에서 랜덤값을 얻는 방법은 간단합니다.
엔진을 선언하고, 어떤 분포를 갖는지 명시해주고 호출해주면 됩니다.

#include <random>

void funcInt()
{
std::random_device rd;
std::mt19937 rEngine(rd()); // 엔진에 시드를 세팅.
std::uniform_int_distribution<> dist(-3,3); // 범위지정. [-3, 3] 범위입니다.
auto result = dist(rEngine); // [-3,3] 중에 정수값이 하나 택하여 들어가게 됩니다.
}

기본적인 형태는 위와 같습니다. 시드를 세팅 하지 않으면 매번 같은 결과가 나옵니다.

여기서 실수형태의 범위가 필요하면 std::uniform_real_distribution<> dist(-3.0, 3.0); 으로 범위를 지정하시면 됩니다.

 

이외에도 아래와 같이 정규분포를 갖는 랜덤값도 생성가능합니다.

 

iOS Simulator Screen shot 2013

 

 

std::normal_distribution<> dist(24060.0); // 240 평균이고 60.0 의 분산을 갖는 랜덤 분포

로 선언하시고 쓰시면 됩니다.

이외에도 다른 분포들이 많습니다

bernoulli_distribution
binomial_distribution
cauchy_distribution
chi_squared_distribution
discrete_distribution
exponential_distribution
extreme_value_distribution
fisher_f_distribution
gamma_distribution
geometric_distribution
lognormal_distribution
negative_binomial_distribution
normal_distribution
piecewise_constant_distribution
piecewise_linear_distribution
poisson_distribution
student_t_distribution
uniform_int_distribution
uniform_real_distribution
weibull_distribution

골라서 쓰세요!

출처 : http://cafe.naver.com/cocos2dxusers/13988


신고
Posted by 우엉 여왕님!! ghostkyow

티스토리 툴바