[C언어] 난수 생성 함수 rand, srand 사용법 및 중복 없는 난수 생성

C/기타 함수2013. 11. 29. 05:49



※요약

rand : 예측할 수 없는 하나의 난수를 생성한다.

난수의 범위는 0~RAND_MAX 까지이며 RAND_MAX는 0x7fff이므로 , 결국 난수의 범위는 0~32767 이다.

srand : rand 함수만으로 생성되는 난수는 일정한데 

srand를 이용, 시간 값을 매개로 초기화하면 일정하지 않고 불규칙적인 난수가 생성된다.



※함수 원형 및 설명

int rand( void );

void srand( unsigned int seed );



※사용법 ( 사용법 부분은 "C언어를 배우자" 카페의 라이터님의 게시물을 허락 후 옮겼습니다. )

1. 기본 사용법

i = rand()%n

이것은 0 ~ n-1 범위의 난수를 i에 대입합니다.

예를 들어 n=6 이라 하면 0, 1, 2, 3, 4, 5, 중 하나가 i에 대입되는 셈이지요.

 


2. 기본 응용

i = rand()%n + m

이것은 1번을 응용한 것으로, 0+m ~ n-1+m 범위의 난수를 i에 대입합니다.

예를 들어 n=6, m=4 라 하면 4, 5, 6, 7, 8, 9, 중 하나가 i에 대입되는 셈이지요.

다른 예로 n=5, m= -2 라 하면 -2, -1, 0, 1, 2 중 하나가 i에 대입되는 것이죠.

 

 

i = rand()%n * m

이것은 0 ~ n-1 으로 나올 수 있는 수에 m을 곱한 수를 i에 대입하는 식입니다.

예를 들어 n=4, m=2 이라 하면, 0, 2, 4, 6 중 하나가 i에 대입되는 것이죠.

m의 값을 2로 주면 2의 배수, 3을 주면 3의 배수가 나오는군요.

 

이제 두개를 섞어서,


i = (rand()%n + m) * o

이것은 0+m ~ n+m-1 으로 나올 수 있는 난수에 o을 곱한 수를 i에 대입하는 식입니다.

n = 3, m = 2, o = 4 라 하면, 8, 12, 16 중 하나가 i에 대입되는 것이죠.

이것도 어떤 수의 배수를 사용할 때 용이하겠습니다.


i = rand()%n * m + o

이건 0 ~ n-1 으로 나올 수 있는 수에 m을 곱하고, o를 더한 수를 i에 대입하는 식입니다.

n = 3, m = 2, o = 5 라 하면, 5, 7, 9 중 하나가 i에 대입되는 것이죠. 




3. 기본 심화 응용

i = rand()%n * m + o

이 식을 다시 한번 봅시다.

n의 값이야 0 이상으로 주고, m = 2, o = 0 으로 주면, 짝수가 생성됩니다.

m = 2, o = 1 으로 주면, 홀수가 생성되는것을 볼 수 있습니다.



4. 배열을 이용한 응용 - 1 기본 기법

배열을 이용한 응용이라...  rand() 의 마법을 보실 수 있겠군요.

int ar[MAX] = {원하는 원소 MAX개}; 라는 배열이 있다고 생각하고...


i = ar[rand()%MAX]

이 식은 상당히 유용합니다. 예를 들어보는게 제일 빠르겠지요.

MAX = 4로 잡고, ar 배열의 원소를 { 2, 5, 9, 3214324 } 이라 합시다.

저 4개의 숫자는 규칙성도 뭣도 없어서 위에서 본 응용법으로는 뽑아 낼 수가 없지요.

하지만 배열과 4번의 식을 이용한다면?


rand()%MAX 는 0 ~ MAX-1 범위의 수를 뽑아낼 것입니다.

MAX 가 4니까 0, 1, 2, 3, 이 나오겠지요? 그런 다음에 ar[rand()%MAX] 으로 ar배열 속에 있는 원소에 접근하는데...

rand()%MAX 가

0 이면 2

1 이면 5

2 이면 9

3 이면 3214324

를 i 에 대입할 수 있는거죠.

 

이렇게 배열을 이용하면 rand() 함수 한번의 호출로 불규칙한 수를 난수로 뽑아내 쓸 수 있는거죠.

응용법은... 아래쪽에 있습니다.



5. 배열을 이용한 응용 - 2 자세한 응용

이번엔 다른 배열 응용법을 알아봅시다.


복권을 만들었는데, 1/10 의 확률로 1등, 3/10의 확률로 2등, 6/10의 확률로 3등이라고 하고,

rand() 를 이용해 랜덤하게 추첨을 하려고 하는데..


" 3가지 경우가 있으니 rand()%3 해서 0 이면 1등, 1이면 2등... "

이런 생각은 떙 !!!!

0, 1, 2, 가 나올 확률은 같습니다!!!


" 그럼 rand()%10 해서 0 은 1등, 1~3 은 2등, 4~9 는 3등으로... 이런 복잡한 걸"

정답인데, 이러면 조건문이 들어가야 하는 복잡함이!!!!


배열 하나면 됩니다.

int ar[10] = { 1, 2, 2, 2, 3, 3, 3, 3, 3, 3 }

이런 배열 하나 준비 하시고!!!!


i = ar[rand()%10]

아하하하하하, 이러면 끝 !! i에는 추첨된 등수가 들어가게 됩니다.

설명해 드리죠.


rand()%10 으로 나올 수 있는 수는

0, 1, 2, 3, 4, 5, 6, 7, 8, 9 이고, 이 10가지 수가 나올 수 있는 수학적 확률은 같습니다.


그러나, 0이 나올 확률은 1/10,

1, 2, 3 이 나올 학률은 3/10,

4, 5, 6, 7, 8, 9 가 나올 확률은 6/10  !!!


배열 원소를 보면, ar[0] 은 1이고

ar[1] ~ ar[3]은 2,   ar[4] ~ ar[9]는 3!!!!


배열로는 이런일이 가능합니다 !!!




※예제

아래는 중복 없는 난수 생성 예제입니다. 

로또 번호 생성 예제이며, 제가 배포하는 로또 프로그램에서 일부 가져와 군더더기를 제거하고  예제에 맞게 수정했습니다.

중복을 방지하기 위해 생성 여부에 대한 일종의 인덱스를 둬서 따로 검사하지 않아도 중복을 방지할 수 있게 했습니다.

흔히 사용하는 배열 요소를 처음부터 끝까지 조사 방식은 배열 개수에 따라 속도가 엄청 느려지므로 추천하지 않습니다.

이렇게 인덱스를 두는 방법은 에라토스테네스의 체를 구할 때도 이용되고 

데이터를 캐싱할 때 이미 불러들인 데이터인지 판별할 때도 사용됩니다.

#include <stdio.h>
#include <stdlib.h>       //srand
#include <time.h>     //time
 
//#define _MAX 6
const int nMAX = 6;
 
void BubbleSort( int *nArr, int nArrSize );
 
int main( )
{
    //변수 생성 및 초기화 부분
    srand( (unsigned)time(NULL) );              //srand로 초기화
 
    int nLottoNum[nMAX] = { 0, };               //생성된 로또 번호를 저장할 변수
    bool bCheckExistOfNum[45] = { false, };     //생성된 번호가 중복인지 체크할 변수(인덱스)
 
    //번호 생성 부분
    for( int i=0 ; i<nMAX ; )                    //번호가 6개 생성될 때까지 돈다.
    {
        int nTemp = rand()%45;                  //0~44 범위의 번호를 생성한다.
 
        if( bCheckExistOfNum[nTemp] == false )  //중복 여부 판단
        {
            bCheckExistOfNum[nTemp] = true;     //번호가 중복 생성되지 않게 존재 여부를 true로 한다.
            nLottoNum[i] = nTemp+1;             //+1을 안해주면 범위가 0~44이다.
            ++i;                                //증감 연산을 for문에서 하지 않고 여기서 한다.
        }
    }
 
    //버블 정렬
    BubbleSort( nLottoNum, sizeof(nLottoNum)/sizeof(int) );
 
    //출력 부분
    for( int i=0 ; i<nMAX ; ++i )
    {
        printf( "%2d ", nLottoNum[i] );
    }
 
    return 0;
}
 
void BubbleSort( int *nArr, int nArrSize )
{
    for( int i=0 ; i<nArrSize-1 ; i++ )
    {
        for( int j=0 ; j<nArrSize-(i+1) ; j++ )
        {
            if( nArr[j] > nArr[j+1] )    //꺽쇠 방향으로 오름 차순, 내름 차순 결정
            {//값 교환 부분
                int temp = nArr[j];
                nArr[j] = nArr[j+1];
                nArr[j+1] = temp;
            }
        }
    }
}


작성자

Posted by 사용자 오뇽

태그

댓글 영역

  • 프로필 사진
    maze
    2014.08.02 15:53

    기발하네요.. 하나 배웠습니다..

    임시로 저장하는 temp 변수를 사용하지않는 방법으로 함수를 하나 만들어봤어요..

    int * gen_rand_data (int max) {
    int i;
    int *data;
    int check[max];

    if ( max > 100000 )
    return NULL;

    memset(check, -1, max * sizeof(int));

    data = malloc(max * sizeof(int));
    if ( !data )
    goto out;

    srand(time(NULL));

    /*int temp;
    for (i = 0; i < max;) {
    temp = rand() % max;
    if ( check[temp] == -1 ) {
    data[i] = temp;
    check[temp] = 1;
    i++;
    }
    }*/

    // temp 변수를 사용하지 않는 방법
    for (i = 0; i < max;) {
    data[i] = rand() % max;
    if ( check[data[i]] == -1 ) {
    check[data[i]] = 1;
    i++;
    }
    }

    out:
    return data;
    }

    • 프로필 사진
      2014.08.05 03:43 신고

      ㅡㅡ;;
      일단 컴파일이 되는지 확인하셔야죠!?ㅋㅋ
      그리고 temp변수는 없애는 것보다는 다른 방법으로 구현 해보는게 더 도움이 될것 같네요~

  • 프로필 사진
    2017.10.27 20:07

    어렵지만 쉽고 좋다 번역해서 보고있다

  • 프로필 사진
    희철맨
    2018.01.09 19:35

    와 중복없는 난수 생성 보고있었는데
    전혀 생각지도 못한 방법이었어요 진짜 잘 배워갑니다
    정리해서 블로그에 새로 올려도될까요?
    출처 올리겠습니다