ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 난수, rand() 함수
    @ 16. 1 ~ 17. 1/C++ 2015. 11. 17. 19:25

    나머지 기반 난수 발생기의 결점은

    기본적으로 rand함수 때문에 생기는 것이다.

    rand함수는 20억 주기의 값들을 돌려주지만, 나머지를 적용하면 그 주기가 줄어든다. rand 함수가 생성한 수들의 하위 비트들에는

    패턴이 존재하며, 그 패턴의 주기는 20억보다 훨씬 짧다. 그런데 나머지는 본질적으로 수의 상위 비트들을 잘라내므로 결과적으로

    나머지를 통해 얻은 난수들의 주기가 원래의 난수들보다 훨씬 짧아지게 된다. 즉, 패턴이 좀 더 자주 반복되는 것이다.

    패턴이 좀 더 나은 난수 생성기를 만들기 위해서는 하위 비트들이 아니라 상위비트들을 보존 활용해야한다.

     

    나누기를 이용한 범위 결정

    상위 비트를 보존 활용하는 가장 쉬운 방법은 나누기 연산자를 사용하는 것이다.

    number = ((6 * rand()) / (RAND_MAX+1)) + 1; //1에서 6사이의 난수를 만들어내는 예이다.

    rand가 돌려준 값에 6을 곱하고 그것을 최대 크기 더하기 1로 나눈다. 그러면 0 ~ 5사이의 수가 된다.

    거기에 1을 더하니까 1 ~ 6의 수가 된다.

     

    int RandomRange(int p_min, int p_max)
    {
    	int difference = (p_max - p_min) + 1;
    	return ((difference * rand()) / (RAND_MAX + 1)) + p_min;
    }
    

     

    한가지 단점이 있다. 난수의 범위 크기(6이) 2^17보다 커서는 안된다. 크면 오버플로우가 일어남.

    rand가 15비트의 수를 돌려주기 때문이다. int는 32비트이며 32비트 한계안에서 15비트수에 곱할 수 있는 최대한계는 17비트이다.

    예를들어 18비트이면 오버플로우가 일어난다. 어쨌든 범위는 131072이상이면 안됨.

     

    이산적인 수는 1,2,3처럼 정수인데..

    비율을 난수로 얻어야 할 때가 있다. 0.1 0.2 등등..0.0 부터 1.0 사이의 소수..

    어떻게 해야할까?

    rand가 생성하는 난수의 범위는 0에서 RAND_MAX이므로 rand가 돌려준 값을 RAND_MAX로 나누면 원하는 결과를 얻는다.

    예를들어 rand가 RAND_MAX를 돌려줬다면 RAND_MAX/RAND_MAX는 1이다.

    마찬가지로 0/RAND_MAX는? 0이다.

    float RandomPercent()
    {
    	//여기서 중요한것은 정수 나누기가 일어나서는 안된다.
    	//정수 나누기를 피하기 위해서 rand의 결과와 RAND_MAX 모두를 먼저 float으로 형변환한 후 나누기 수행
    	return (float)rand() / (float)RAND_MAX;
    }

     

    비율을 생성할 수 있다면 임의의 범위의 부동소수점 난수도 쉽게 만들 수 있다.

    위의 RandomPercent함수로 0~1사이의 부동 소수점 값을 얻고 범위를 적절히 조정하면 된다.

    예를 들어 0.0 에서 5.0사이의 부동소수점 난수를 얻고자 하면. RandomPercent 결과에 5.0을 곱하기만 하면된다.

    float num = RandomPercent() * 5.0f;

    또 예를 들어 1에서 6사이의 실수를 얻고자 할때는 ? 정수와 마찬가지로 결과에 1을 더해주면 된다.

    float num = RandomPercent() * 5.0f + 1.0f;
    float RandomRangeF(float p_min, float p_max)
    {
    	float difference = (p_max - p_min);
    	return (RandomPercent() * difference) + p_min;
    }

    위의 함수는 임의의 범위의 부동소수점 난수를 생성하는 함수(위의 RandomPercent()와 같이..)

     

    그런데 지금까지 한 난수의 경우 균일한 확률내에서 난수가 발생하게 된다.

    예를들어 1~6까지이면 1/6씩의 확률로 난수가 발생하게 된다.

     

    그러나 실제 세상을 예로 들면 키가 가장 큰 사람과 가장 작은 사람은 보통 사람들 보다 확률이 매우 적다

    그래서 우리는 비선형 난수 분포도 필요하다

     

    비선형 난수 분포를 보이는 난수 발생에서 가장 간단한 방법은 두 난수를 더하면 된다.

    int x = RandomRange(0, 4) + RandomRange(0, 4);

    x의 범위가 그러면 0 ~ 8까지이고...난 수의 분포는 더이상 균일하지 않게 된다. 0이 나올 확률이 4가 나올 확률보다 훨씬 작아진다.

    첫번째 난수나 두번째 난수의 결과에 따라 최종 결과의 값이 확 달라진다.

    모든 가능한 경우들을 2차원 배열 형태로 표시하면..

     

     

     

     0

     1

     1

     1

     2

     2

     2

     3

     5

     6

     3

     3

     4

     5

     7

     4

     4

     5

     6

    7

    파란색은 첫번째 난수의 결과, 빨간색은 두번째 난수의 결과

    최종 결과가 0이 나올 경우는 0,0 딱 하나이다. 즉 확률은 1/25 = 4퍼센트 인것이다.

    그런데 이것도 완만한 곡선과는 거리가 멀다.(그래프를 그린다면..)

    그래서 한개의 난수를 더 더해서.. 세개의 난수를 더하면 좀 더 곡선에 가깝게 된다.

    그러니까 0 ~ 9까지의 난수가 필요하다면

    0 ~ 3사이의 난수를 3개 더한 결과를 이용하라는 것..

    그럼? 4개 더한건? 더 좋지!

     

Designed by Tistory.