Tkuji Nishimura 와 Makoto Matsumoto가 고안한 Mersenne-Twister 의사 난수 발생 알고리즘 사용
/* 주기 매개 변수들 */
#define CMATH_N 624
#define CMATH_M 397
#define CMATH_MATRIX_A 0x9908b0df // 상수 벡터 a
#define CMATH_UPPER_MASK 0x80000000 // 최상위 w-r 비트들
#define CMATH_LOWER_MASK 0x7fffffff // 최하위 r 비트들
/* 조절용 매개변수들 */
#define CMATH_TEMPERING_MASK_B 0x9d2c5680
#define CMATH_TEMPERING_MASK_C 0xefc60000
#define CMATH_TEMPERING_SHIFT_U(y) (y >> 11)
#define CMATH_TEMPERING_SHIFT_S(y) (y << 7 )
#define CMATH_TEMPERING_SHIFT_T(y) (y << 15 )
#define CMATH_TEMPERING_SHIFT_L(y) (y >> 18 )
class CRandom
{
// 데이터
unsigned int rseed;
unsigned long mt[CMATH_N]; // 상태 벡터를 위한 배열
int mti; // mti == N+1은 mt[N]이 초기화 되지 않았음을 뜻한다.
// 함수
public:
CRandom( void );
unsigned int Random( unsigned int n );
void SetRandomSeed( unsigned int n );
unsigned int GetRandomSeed( void );
void Randomize( void );
};
CRandom::CRandom( void )
{
rseed = 1;
mti = CMATH_N + 1;
}
// 0에서 n 사이의 (n은 제외) 한 난수를 돌려준다.
unsigned int CRandom::Random( unsigned int n )
{
unsigned long y;
static unsigned long mag01[2] = { 0x0, CMATH_MATRIX_A };
if( n == 0 )
return (0);
// x가 0이나 1이면 mag01[x] = x * MATRIX_A
if( mti >= CMATH_N ) // 한번에 N개의 워드를 생성
{
int kk;
if( mti == CMATH_N+1 ) // sgenrand()가 호출된 적이 없다면
SetRandomSeed(4357); // 기본 초기 종자값을 사용한다.
for( kk = 0; kk<CMATH_N - CMATH_M; kk++ )
{
y = (mt[kk] & CMATH_UPPER_MASK ) | ( mt[kk+1] & CMATH_LOWER_MASK );
mt[kk] = mt[ kk + CMATH_M ] ^ ( y >> 1 ) ^ mag01[y & 0x1];
}
for(; kk < CMATH_N - 1; kk++ )
{
y = (mt[kk] & CMATH_UPPER_MASK ) | (mt[kk+1] & CMATH_LOWER_MASK );
mt[kk] = mt[kk+(CMATH_M - CMATH_N )] ^ (y >> 1) ^ mag01[y & 0x1];
}
y = ( mt[CMATH_N - 1] & CMATH_UPPER_MASK ) | (mt[0] & MATH_LOWER_MASK);
mt[CMATH_N - 1] = mt[CMATH_M-1] ^ (y >> 1) ^ mag01[y & 0x1];
mti = 0;
}
y = mt[mti++];
y ^= CMATH_TEMPERING_SHIFT_U(y);
y ^= CMATH_TEMPERING_SHIFT_S(y) & CMATH_TEMPERING_MASKB;
y ^= CMATH_TEMPERING_SHIFT_T(y) & CMATH_TEMPERING_MASKC;
y ^= CMATH_TEMPERING_SHIFT_L(y);
return ( y%n);
}
void CRandom:: SetRandomSeed( unsigned int n )
{
// [ KNUTH 1981, The Art of Computer Programming
// Vol. 2 ( 2nd Ed. ), pp102]의 Table 1의
// 줄 25에 나와 있는 생성기를 이용해서
// 초기 종자값들을 mt[N]에 설정한다.
mt[0] = n & 0xffffffff;
for( mti = 1; mti<CMATH_N; mti++ )
mt[mti] = (69069 * mt[mti-1] ) & 0xffffffff;
rseed = n;
}
unsigned int CRandom::GetRandomSeed( void )
{
return (rseed);
}
void CRandom::Randomize(void)
{
SetRandomSeed( time(NULL) );
}
//난수 종자값을 저장, 로드 할수 있다.
// 종자값을 다시 불러오면 이전과 동일한 난수들을 그대로 다시 발생시킬 수 있다.
// 리플레이 기능등을 구현할 때 유용할 것이다.
사용예 >>
CRandom r;
r.Randomize();
unsigned int num = r.Random(100); // 0에서 99 사이의 난수를 얻는다.
게임의 하위 시스템 마다 개별적으로 이 클래스의 인스턴스를 생성해서 사용 하라..
각 시스템별로 난수가 예측가능하므로 다른 시스템을 꺼도, 하나의 시스템의 예측이 가능해져
디버깅과 테스팅에 도움이 된다.
'Development > C/C++' 카테고리의 다른 글
[본문스크랩] Using ASSERT(), VERIFY(), and TRACE() in non-MFC Ap.. (0) | 2011.08.13 |
---|---|
[본문스크랩] 완성형/조합형 한글/한자/특수문자 코드표 (0) | 2011.08.13 |
[64비트 윈도우 프로그래밍] ③ 64비트 프로그램으로 가는 길 (0) | 2011.08.13 |
[64비트 윈도우 프로그래밍] ② 64비트 윈도우 파악하기 (0) | 2011.08.13 |
[64비트 윈도우 프로그래밍] ① 32비트 프로그램을 엄호하라 (0) | 2011.08.13 |