C 언어 | 고급 기능 | 난수 - rand(), srand()

C 언어의 표준 함수를 사용하여 적당한 값을 얻는 방법을 설명한다.

랜덤 값 얻기

많은 게임에서는 사용자가 예상할 수 없는 결과를 얻어야 한다. 또는 자연 과학 및 사회 과학 등의 시뮬레이션을 수행하는 프로그램에서도 일정하게 예기치 않은 변화를 준비해야 한다. 일반적으로 비즈니스 응용 프로그램은 필요하지 않겠지만, 프로그램의 종류에 따라서는 억지로 부정확한 결과를 산출해야 하는 경우도 있는 것이다.

이것을 실현하려면 난수를 구해야 한다. 난수를 사용하기 위한 함수는 stdlib.h 헤더 파일에 선언되어 있다. 난수를 이용하면, 게임 프로그래밍은 물론, 반복 처리와 난수를 사용한 문제를 처리하는 몬테 카를로 방법(Monte Carlo method)이라는 수학 기법을 사용한 프로그램 등, 다양한 용도가 있을 수 있다.

난수를 구하려면 rand() 함수를 사용한다.

rand() 함수

int rand( void );

rand() 함수는 0부터 RAND_MAX 범위 내에서 int 형 난수를 반환한다. RAND_MAX는 stdlib.h 헤더 파일에 정의되어 있는 rand() 함수가 반환하는 최대 값을 나타내는 상수이다.

이 함수가 반환하는 값은 완전한 난수가 아닌, 특정 계산식으로 산출한 적당한 값이다. 일반적으로 컴퓨터는 난수를 발생시키는 하드웨어를 가지고 있지 않다. 그래서 난수를 얻기 위해 기본이 되는 값을 계산하고 난수를 생성한다. rand() 함수는 생성된 난수를 얻기 위한 함수이다.

코드1

#include <stdio.h>
#include <stdlib.h>

int main() {
  int iCount;

 printf("/// 난수의 최대값 = %d ///\n" , RAND_MAX);

 for(iCount = 0 ; iCount < 10 ; iCount++)
    printf("난수 열 %d = %d\n" , iCount , rand());

  return 0;
}

코드1을 실행하면 RAND_MAX 상수 값과 rand() 함수에서 얻은 의사 난수를 표시한다.

(컴퓨터에서 만들 수 있는 난수는 임의적일 수 밖에 없어서 난수처럼 보이지만 완전한 무작위는 아니다. 이를 의사난수라고 한다.)

표준은 stdlib.h에 정의되어 있는 매크로 RAND_MAX는 32767 이상임을 보증한다. 표시되는 의사 난수를 보면 확실히 rand() 함수는 0 ~ 32767 이내의 적당한 값을 반환하는 것을 확인할 수 있다.

그러나 코드1을 다시 실행하면 똑같은 결과를 얻는다. 난수 열는 확실히 적당한 값이지만, 프로그램을 실행할 때마다 같은 난수 열을 반환하도록 것은 이 프로그램의 실행 결과가 항상 같으므로 임의 처리로는 사용할 수 없다. 왜, rand() 함수는 같은 난수 열을 반환할까?

실은 의사 난수를 발생시키기 위해서는 “씨"을 뿌려야 한다. 난수 열은 기본이 되는 값을 사용하여 생성된다. 사용되는 계산식은 항상 동일하기 때문에, 기본 값이 동일하면 같은 난수 열이 생성되어 버린다. 코드1이 항상 같은 결과가 되는 것은 난수를 생성하기 위한 기준 값이 같았기 때문이다. 이 기준 값을 시드(Seed)라고 한다. 난수를 생성하기 위한 시드를 설정하려면 srand() 함수를 사용한다.

srand() 함수

void srand( unsigned int seed );

seed에는 난수를 생성하기 위한 시드 값을 지정한다. 난수는 seed로 지정된 값을 초기 값으로 난수를 생성한다. seed에 1을 지정하면 난수가 초기화된다. srand ()를 호출하기 전에 rand()가 호출된 경우 seed를 1로 난수 열이 생성된다.

프로그램을 실행할 때마다 예기치 못한 적당한 값을 얻기 위해서는 항상 다른 시드를 부여해야 한다. 난수의 생성에 가장 많이 사용되는 초기 값은 시간이다. time() 함수를 사용하여 얻은 값은 항상 다른 값이 되기 때문에 개발자도 예상할 수 없는 적당한 값을 동적으로 얻을 수 있다.

코드2

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

int main() {
  int iCount;

 srand(time(NULL));
  for(iCount = 0 ; iCount < 10 ; iCount++)
    printf("난수 열 %d = %d\n" , iCount , rand());

  return 0;
}

코드2는 rand() 함수를 호출하기 전에 srand() 함수를 이용하여 난수의 초기 값을 지정한다. time() 함수를 사용하여 현재 시간 값을 시드로 지정하고 있기 때문에 이 프로그램을 실행할 때마다 다른 초기 값을 난수의 생성에 사용한다. 이번에는 실행할 때마다 적당한 값을 얻을 수 있을 것이다.

그러나 이 상태에서는 특정 범위의 난수를 얻을 수 없다. 일반적으로 32767까지의 넓은 범위의 난수를 사용하지 않는다. 반대로, 경우에 따라서는 32767 이상의 넓은 범위의 난수를 얻고 싶을 때도 있을 것이다. 이것을 실현하려면 rand() 함수에서 얻은 결과를 계산하고 해결한다. 지정 범위의 난수를 얻는 가장 쉬운 방법은 난수와 최대 값을 나눈 나머지 값을 얻는 방법이다. 0~9의 난수가 필요한 경우는 rand() % 10를 계산하여 얻을 수 있다.

하지만, 이 방법은 그다지 권장되지 않는다. 더 좋은 방법으로, 0.0에서 1.0까지의 부동 소수점형 의사 난수를 사용하는 방법이 선호된다. 0~1 사이의 부동 소수점 의사 난수를 얻을 수 있다면, 이것은 최대 값을 곱하여 특정 범위의 난수를 얻을 수 있다. 이 방법이면 난수의 최대 값에 얽매이지는 않는다. 0.0에서 1.0 사이의 난수를 얻으려면 다음과 같은 매크로를 작성하는 것이 좋다.

#define random() ((double)rand() / (RAND_MAX + 1))

random () 매크로 함수는 0.0에서 1.0 사이의 난수를 반환한다.

코드3

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#undef random
#define random() ((double)rand() / (RAND_MAX + 1))

int main() {
 int iCount;
 srand(time(NULL));
  for(iCount = 0 ; iCount < 10 ; iCount++)
    printf("난수 열 %d = %g\n" , iCount , random());
  return 0;
}

코드3은 0.0에서 1.0까지의 double 형의 난수를 반환 random() 매크로 함수를 정의하고 있다. 덧붙여서 호환성을 위해 일부 처리계에서는 매크로 함수 randam()가 이미 정의되어 있을 수 있다. 그래서 #undef 지시문을 사용하여 random()가 중복되지 않는 것을 보장하고 있다. 불필요한 수고가 싫다면 이름이 충돌하지 않도록 다른 이름으로 매크로 함수를 정의해도 상관없다.




최종 수정 : 2017-11-26