Computer >> 컴퓨터 >  >> 프로그래밍 >> JavaScript

JavaScript random7() 함수로 random10() 구현하기

문제 정의

다음과 같은 자바스크립트 화살표 함수가 있다고 가정해 보겠습니다.

const random7 = () => Math.ceil(Math.random() * 7);

이 함수는 호출할 때마다 1 이상 7 이하의 정수 난수를 반환합니다. 즉, 0은 제외하고 7까지 포함하는 범위의 값을 얻습니다.

요구 사항은 별도의 라이브러리나 외부 도구 없이 오직 이 random7() 함수만을 사용해, 1 이상 10 이하의 난수를 반환하는 random10() 함수를 작성하는 것입니다. 함수는 인자를 받지 않으며, 자바스크립트 내장 난수 생성기나 서드파티 라이브러리에 직접 의존해서는 안 됩니다.

예제 코드

가장 직관적인 접근은 random7()을 여러 번 호출해 합계를 구한 뒤, 10으로 나눈 나머지를 활용하는 것입니다.

const random7 = () => Math.ceil(Math.random() * 7);

const random10 = () => {
    let sum = 0; // 반드시 0으로 초기화
    for (let i = 0; i < 50; i++) {
        sum += random7();
    }
    return (sum % 10) + 1;
};

console.log(random10()); // 1~10 사이의 정수 출력

코드 설명

동작 원리를 단계별로 살펴보면 다음과 같습니다.

  1. 난수 합산: random7()을 50번 호출해 결과를 모두 더합니다. 50이라는 반복 횟수는 임의로 정한 값이며 다른 수를 써도 무방합니다.
  2. 모듈로 연산: 합계를 10으로 나눈 나머지(sum % 10)는 0부터 9 사이의 값을 가집니다.
  3. 범위 보정: 여기에 1을 더해 최종 결과를 1~10 범위로 맞춥니다.

주의: 원본 코드의 NaN 문제

원본 예제에서는 변수를 let sum;처럼 선언만 하고 초기화하지 않았습니다. 이렇게 하면 sum은 undefined 상태로 시작하고, 여기에 숫자를 더하는 순간 결과는 NaN(Not a Number)이 됩니다. 실제 콘솔 출력은 다음과 같습니다.

NaN

따라서 let sum = 0;처럼 반드시 0으로 초기화해야 의도한 대로 동작합니다.

더 나은 대안: 기각 샘플링

합계 방식은 간단하지만 통계적 약점이 있습니다. 중심극한정리에 따라 균등 분포 난수를 여러 번 더하면 그 합이 정규 분포에 가까워지므로, 10으로 나눈 나머지는 완전히 균등하지 않고 특정 숫자가 더 자주 나오는 편향이 생길 수 있습니다.

수학적으로 완벽한 균등 분포가 필요하다면 기각 샘플링(rejection sampling) 기법을 사용하는 것이 좋습니다.

const random10 = () => {
    let idx;
    do {
        // random7() 두 번 호출로 1~49 범위의 균등 난수 생성
        idx = (random7() - 1) * 7 + random7();
    } while (idx > 40); // 41~49는 기각 후 재추출
    return ((idx - 1) % 10) + 1; // 1~10으로 매핑
};

이 방식은 7 × 7 = 49개의 조합 중 40개만 사용하고 나머지는 버려 다시 뽑음으로써, 모든 숫자가 정확히 1/10의 확률로 선택되도록 보장합니다. 평균 약 2.45번의 random7() 호출로 결과를 얻을 수 있어 효율성 면에서도 우수합니다.

마무리

random7()으로 random10()을 만드는 문제는 코딩 인터뷰에 자주 등장하는 확률·난수 유형의 대표 문제입니다. 합계와 모듈로 연산을 이용한 간단한 구현부터 편향을 제거하는 기각 샘플링까지, 목적과 요구 수준에 맞는 방법을 골라 적용해 보시기 바랍니다.