피씨컴의 기울어진 공관

난수 출력함수 rand() / srand()

 

프로그램상 난수를 출력하는것 같지만 일정한 난수열을 가진 표를 가지고 이표에서 값을 하나씩 출력한다.

이 표를 seed값이라하고, 이 표를는 매번 같은 값을 갖게 된다. 그렇기에 rand함수는 주기성을 같게 되고 주기적으로 반복된다.

하지만 이 seed 값을 달리하면 새로운 난수표가 생성된다.

seed 값을 결정하는 함수는 srand()로써 stdlib.h 헤더파일에 존재한다.

 

 

하지만 값을 다르게 할순없다. 값을 다르게 하기위해선 시간의 개념을 추가해야한다.

 time.h 헤더파일을 추가시키고 srand(time(NULL)) 를 사용한다.
이렇게하면 매번 다른 시간대의 값으로 seed값을 정하니 절대로 같은 난수표를 결정할수 없게된다.


이제 이 난수표에서 난수값을 뽑아내는 함수 rand()를 활용하면 완벽한 난수구현 함수가 된다.

하지만 rand()가 리턴하는 난수의 범위는 0~0x7fff  즉, 0~32767의 값을 리턴한다. (0 ~ RAND_MAX)


원하는 범위내의 값을 리턴받고싶다면 rand()%N 과같이 사용한다 N은 리턴범위로써 0~N-1까지 리턴받는다
만일 0~10까지 리턴받고싶다면 rand()%11 로 하면된다. 이를 응용하면 더욱더 효율적으로 값을 리턴받을수있다. 가령 5~10까지 리턴받고싶을땐 어떻게하는가? 수학을 배워봤는가? 그렇다면 계산순서가 어떻게 되는지는 알고있을것이다. 마찬가지로 rand()%11+5 이렇게하면 5~10까지의 값을 리턴받게 된다. 물론 -값도 가능하다

 

여기서 앞서 rand 함수에는 주기성이 있고 주기적으로 반복 된다고 하였는데 일반적으로 알려진  c++  rand함수의 주기는

2^32(== 4294967296) 라고 한다 그러나 어떤 사람이 실제 실험해 보니 2^31 (== 2147483648) 라 하기도 한다.

 

그러나 rand 함수가 실제 난수표를 이용하여 난수를 발생시키는 것은 아니다

2147483648개의 원소를 저장하려면 자료형을 short로 해도 한 seed당 8GB정도이며 그렇게 봤을때

seed가 10개가 된다면 80GB의 용량을 차지해야 하는데... 이러면 당연 활용에 문제가 될것이다.

 

결국 rand 함수는 아래 rand.c의 소스파일을 보면 static long holdrand = 1L; 전역 변수를 기준으로

rand 함수 안에서 단순한 곱셈식과 덧셈, 쉬프트 연산 등을 통하여 난수를 발생시킨다.

이때 이 난수 발생식의 주기가 2147483648를 가지게 되어 난수 자체의 주기가 생긴것이지 난수표를 이용하여

난수를 발생시킨 것이라고 보기는 힘들다
 

 

다음은 rand.c 의 내용

 

#ifndef _MT
static long holdrand = 1L;
#endif  /* _MT */

 

int __cdecl rand (void)
{
#ifdef _MT

        _ptiddata ptd = _getptd();

        return( ((ptd->_holdrand = ptd->_holdrand * 214013L
            + 2531011L) >> 16) & 0x7fff );

#else  /* _MT */
        return(((holdrand = holdrand * 214013L + 2531011L) >> 16) & 0x7fff);
#endif  /* _MT */
}

 

void __cdecl srand (unsigned int seed)
{
#ifdef _MT

        _getptd()->_holdrand = (unsigned long)seed;

#else  /* _MT */
        holdrand = (long)seed;
#endif  /* _MT */
}

'프로그래밍 > 참고' 카테고리의 다른 글

함수  (0) 2013.02.03
지역 변수 / 전역 변수  (0) 2013.01.31
메모리 영역  (0) 2013.01.31
셔플 알고리즘  (0) 2013.01.28
cin  (0) 2013.01.24
Posted by 피씨컴