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 사이의 난수를 얻는다.

게임의 하위 시스템 마다 개별적으로 이 클래스의 인스턴스를 생성해서 사용 하라..

각 시스템별로 난수가 예측가능하므로 다른 시스템을 꺼도, 하나의 시스템의 예측이 가능해져

디버깅과 테스팅에 도움이 된다.

안정적인 DNS서비스 DNSEver DNS server, DNS service
Posted by 키르히아이스

댓글을 달아 주세요