가우시안 분포 즉, 정규분포로 난수를 발생시켜야 하는 경우가 종종 있다.
하지만 우리가 흔히 사용하는 rand() 함수는 균일 분포로 난수를 가져오므로 따로 구현해야 한다.
다행히 정규분포 난수 발생 소스는 인터넷상에서 쉽게 구할수 있기 때문에 별 문제 될 건 없다.
한 가지 딴지를 걸자면 정규분포는 피 적분이 안되기 때문에 반복문을 사용해 난수를 만들어야 한다.
만약, 아주 많은 난수를 한꺼번에 발생시킨다면, 부하가 걸릴 수 있다는 말이다.
물론, 아주 쓸데없는 걱정이다. 그정도로 21세기의 컴퓨터는 나약하지 않다ㅎㅎ
어쨌거나 반복문이 없는 확률 밀도 기반 난수를 쓰고 싶다는 어리석은 생각에
다음과 같은 삼각 분포 난수 함수를 만들어 보았다.
삼각형 그래프는 정적분이 쉬워서 면적을 비율 삼아 난수를 발생시키기 좋다.
function triangularRand($center, $range, $gradient = 1) {
//중심값이 0이 되도록 이동 시킨후 난수를 구하고 다시 되돌림
//근의 공식에 의한 값이 항상 실수가 되게 하기 위함
return triangularOriginRand(0, $range, $range * $gradient) + $center;
}
function triangularOriginRand($center, $range, $top) {
$total = $top * $range; //그래프 총면적
$area = mt_rand(0, $total); //면적 난수
$a = ($top / $range) / 2.0; //정적분 x^2 계수
$start = $center - $range; //최소값
$end = $center + $range; //최대값
//(최소값과 x 사이의 정적분 = 면적 난수) 방정식 구함
//x가 발생시킬 난수
$half = $total / 2;
if ($area < $half) {
//왼쪽 그래프
$value = quadraticRoot($a, $top, -($a * pow($start, 2)) - ($top * $start) - $area);
if ($value != null) {
return round($value[0] >= $start && $value[0] <= $center ? $value[0] : $value[1]);
}
}
else if ($area > $half) {
//오른쪽 그래프
$value = quadraticRoot(-$a, $top, ($a * pow($center, 2)) - ($top * $center) - ($area - $half));
if ($value != null) {
return round($value[0] >= $center && $value[0] <= $end ? $value[0] : $value[1]);
}
}
return $center;
}
//근의 공식
private function quadraticRoot($a, $b, $c) {
$d = pow($b, 2) - (4 * $a * $c);
if ($d < 0) {
return null;
}
$d = sqrt($d);
$a2 = 2 * $a;
return Array((-$b + $d) / $a2, (-$b - $d) / $a2);
}
0에서 100까지의 난수를 200개 발생시키고 각 범위 안의 개수를 확인해보자
echo nl2br("난수\n");
$name = Array('00~19:','20~39:','40~59:','60~79:','80~00:');
$range = array_fill(0, 5, 0);
for ($i = 0; $i < 200; $i++) {
//중심값 50, 양쪽 범위 각각 50
//즉, 0 ~ 100 사이의 난수 발생
$value = triangularRand(50, 50);
if ($value < 20) {
$range[0]++;
}
else if ($value < 40) {
$range[1]++;
}
else if ($value < 60) {
$range[2]++;
}
else if ($value < 80) {
$range[3]++;
}
else {
$range[4]++;
}
echo "$value, ";
if (($i + 1) % 10 == 0) {
echo nl2br("\n");
}
}
echo nl2br("\n난수 갯수\n");
foreach ($range as $i=>$item) {
echo nl2br("$name[$i] $item\n");
}
결과 출력!
-----------
난수
58, 47, 58, 41, 57, 44, 65, 9, 40, 39,
60, 57, 51, 46, 77, 24, 51, 76, 47, 38,
70, 41, 47, 92, 32, 69, 64, 43, 37, 48,
74, 73, 59, 11, 86, 95, 31, 19, 66, 69,
55, 29, 35, 67, 22, 80, 28, 58, 57, 43,
50, 77, 42, 19, 72, 40, 33, 36, 53, 22,
19, 29, 46, 95, 54, 79, 26, 69, 32, 69,
65, 25, 42, 47, 8, 52, 43, 70, 46, 64,
29, 40, 23, 54, 77, 81, 35, 53, 67, 49,
47, 24, 54, 33, 52, 89, 22, 50, 44, 15,
17, 53, 58, 45, 44, 61, 54, 46, 46, 35,
27, 9, 51, 55, 79, 55, 52, 80, 49, 14,
57, 15, 74, 71, 55, 41, 75, 46, 74, 14,
73, 81, 49, 68, 50, 48, 63, 77, 43, 87,
37, 82, 63, 43, 55, 45, 56, 78, 9, 41,
25, 24, 40, 24, 46, 51, 57, 58, 52, 58,
46, 73, 11, 64, 27, 39, 74, 22, 45, 53,
31, 38, 42, 64, 16, 85, 70, 32, 91, 68,
16, 23, 63, 64, 18, 23, 87, 36, 32, 24,
59, 86, 30, 78, 47, 51, 72, 29, 22, 6,
난수 갯수
00~19: 18
20~39: 43
40~59: 80
60~79: 44
80~00: 15
------------
중심값에 가까울수록 더 많은 난수가 분포되어 있다.
도움이 되셨다면~ 정성으로 빚은 저희 앱! 많은 이용 바래요:)
https://meorimal.com/index.html?tab=spaceship
https://meorimal.com/subway.html
'개발 > php, javascript' 카테고리의 다른 글
Int 난수 함수로 Float 난수 깔끔하게 발생시키기 (0) | 2019.12.26 |
---|---|
삼각행렬로 연립방정식 단번에 구하기 (0) | 2019.08.19 |
round()를 5의 배수로도 반올림 해보자 (0) | 2018.11.26 |
php의 json_decode에서 null이 나올경우 살펴봐야 할점 (0) | 2013.11.11 |
javascript 로 애니메이션 움직임 만들기 (0) | 2013.09.02 |