요새 Nature of Code 라는 책을 간간히 보고 있는 중인데,
Javascript의 Math.random 이라는 함수가 단순히 난수를 생성하는 지 또는 정규분포를 나타내는지 궁금하여 알아보게 되었다.
수학적 지식이 뛰어나지는 못해 작성한 것이 정확하진 않을 수 있습니다. 참고만 해주세요... 오류가 있다면 지적 부탁드립니다.
ECMA 공식 문서를 참고해보면, Math.random에 대해 다음과 같이 적혀있다.
US: Returns a Number value with positive sign, greater than or equal to +0𝔽 but strictly less than 1𝔽, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-defined algorithm or strategy. This function takes no arguments.
KO: 구현-정의 알고리즘 또는 전략을 사용하여, 0 ≤ x < 1 범위에서 거의 균등한 분포로 랜덤 혹은 유사하게 양의 기호의 수를 반환한다.
이 함수는 arguments 를 가지지 않는다.
즉, 이 말에 따르면 다음 그림과 같이 Math.random 함수가 정규분포가 아닌 거의 균등한 확률로 난수를 만들어 내는 함수임을 알 수 있다.
그럼, 어떻게 해야 정규분포를 따르게 하는 함수를 만들 수 있을까.
이 부분에 관련해서는 여러 글들을 살펴보았고 간편하게 쓸 수 있을 법한 답변을 위주로 살펴보았다.
1. 만들어 놓은 라이브러리를 사용한다.
https://github.com/errcw/gaussian
GitHub - errcw/gaussian: A JavaScript model of the normal distribution
A JavaScript model of the normal distribution. Contribute to errcw/gaussian development by creating an account on GitHub.
github.com
이미 엄청난 사람들이 만들어 두었다. 그냥 다운받아 쓰기만 하면 된다.
하지만.. 나같이 그냥 라이브러리 같은 것을 사용하고 싶지 않은 사람들을 위해..
2. Math.random을 더해 평균값으로 정규화를 해본다.
function randomG(v) {
var r = 0;
for (var i = v; i > 0; i--) {
r += Math.random();
}
return r / v;
}
가장 간단한 식으로 표현할 수 있다. 하지만 내가 원하는 정도의 분포는 나오지 않았다.
테스트 케이스가 큰 상황에서 사용하면 괜찮을 것 같기도 하다.
3. 척도(Scaled) 정규 분포의 사용
function gaussianRand() {
let rand = 0;
for (let i = 0; i < 6; i += 1) {
rand += Math.random();
}
return rand / 6;
}
function gaussianRandom(start, end) {
return Math.floor(start + gaussianRand() * (end - start + 1));
}
//출처: https://stackoverflow.com/questions/25582882/javascript-math-random-normal-distribution-gaussian-bell-curve/39187274#39187274
표준 편차가 1이 나오지 못한다. 그래서 완전한 정규 분포라고 보긴 어렵다고 한다.
극점에 있는 값들이 잘 안나오는 건 아쉽지만 괜찮은 코드인 것 같다.
4. Box-Muller 변환을 이용한 정규분포
function randn_bm(min, max, skew) {
let u = 0, v = 0;
while (u === 0) u = Math.random() //Converting [0,1) to (0,1)
while (v === 0) v = Math.random()
let num = Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v)
num = num / 10.0 + 0.5 // Translate to 0 -> 1
if (num > 1 || num < 0)
num = randn_bm(min, max, skew) // resample between 0 and 1 if out of range
else {
num = Math.pow(num, skew) // Skew
num *= max - min // Stretch to fill range
num += min // offset to min
}
return num
}
//출처: https://stackoverflow.com/questions/25582882/javascript-math-random-normal-distribution-gaussian-bell-curve/39187274#39187274
이것도 마찬가지로 극 값이 잘 안나오긴 하지만, 평균값을 기준으로 균등하게 잘 나오는 것 같다.
하지만 수식을 이정도로 써야한다면 그냥 라이브러리 쓸 것 같기도 하다.
참고하면 좋은 사이트
Implementing a Simple Skew-normal PRNG in JavaScript
A quick overview of what skew-normal distributions are and a simple way to implement one in JavaScript without additional libraries.
spin.atomicobject.com
JavaScript Math.random Normal distribution (Gaussian bell curve)?
I want to know if the JavaScript function Math.random uses a normal (vs. uniform) distribution or not. If not, how can I get numbers which use a normal distribution? I haven't found a clear answer...
stackoverflow.com
'개발 > Javascript' 카테고리의 다른 글
Reactivity와 Proxy 간단히 알아보기 (0) | 2024.03.30 |
---|---|
만약 영상에 배속기능이 없다면? (ex. 학교 강좌) (4) | 2022.08.12 |
OverRiding(오버라이딩)과 OverLoading(오버로딩) 간단 정리 (0) | 2021.06.05 |
간단하게 훑어보는 함수형 프로그래밍 #1 (0) | 2020.05.04 |
ES6 Class (0) | 2020.04.23 |