난수 출력함수 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 */
}