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

JavaScript에서 숫자를 4개의 난수로 나누는 방법


문제 소개

JavaScript에서 하나의 숫자를 여러 개의 난수로 나눠야 하는 경우가 종종 있습니다. 예를 들어 총 포인트를 사용자들에게 무작위로 분배하거나, 게임에서 아이템 수량을 나누는 상황 등이 대표적입니다.

이번 글에서는 다음 조건을 만족하는 JavaScript 함수를 작성해 보겠습니다.

  • 첫 번째 인자로 전체 합계가 될 숫자를 받습니다.
  • 두 번째 인자로 각 숫자가 넘지 말아야 할 최댓값을 받습니다.
  • 생성된 네 개의 난수를 모두 더하면 첫 번째 인자와 같아야 합니다.
  • 네 개의 숫자 중 어느 하나도 두 번째 인자(최댓값)를 초과해서는 안 됩니다.

예를 들어 함수에 다음과 같은 인자가 주어졌다고 가정해 봅시다 −

const n = 10;
const max = 4;

그렇다면 아래와 같은 결과도 유효한 조합입니다 −

const output = [3, 2, 3, 2];

단, 숫자의 중복은 허용됩니다.

구현 코드

이 문제를 해결하는 코드는 다음과 같습니다 −

const total = 10;
const max = 4;

const fillWithRandom = (max, total, len = 4) => {
   let arr = new Array(len);
   let sum = 0;
   do {
      for (let i = 0; i < len; i++) {
         arr[i] = Math.random();
      }
      sum = arr.reduce((acc, val) => acc + val, 0);
      const scale = (total - len) / sum;
      arr = arr.map(val => Math.min(max, Math.round(val * scale) + 1));
      sum = arr.reduce((acc, val) => acc + val, 0);
   } while (sum - total);
   return arr;
};

console.log(fillWithRandom(max, total));

실행 결과

콘솔에 출력되는 결과는 다음과 같습니다 −

[ 3, 3, 2, 2 ]

난수를 기반으로 하기 때문에 실행할 때마다 출력 결과는 달라집니다.

코드 동작 원리

이 알고리즘이 어떻게 작동하는지 단계별로 살펴보겠습니다.

  1. 난수 생성: 길이가 4인 배열을 만들고 각 요소에 Math.random()으로 0과 1 사이의 실수 난수를 채웁니다.
  2. 비율 계산: 배열의 합계를 구한 뒤, 목표 총합(total)에서 요소 개수(len)를 뺀 값을 합계로 나눠 스케일 계수를 계산합니다.
  3. 정수 변환 및 최댓값 제한: 각 난수에 스케일을 곱하고 1을 더한 후 반올림해 정수로 만듭니다. Math.min()을 사용해 값이 최댓값을 넘지 않도록 제한합니다.
  4. 검증 반복: 결과 배열의 합이 목표 총합과 일치하지 않으면 do...while 반복문에 의해 처음부터 과정을 다시 수행합니다.

이처럼 재시도 방식(rejection sampling)을 활용하면 조건을 만족하는 조합을 확실하게 얻을 수 있습니다. 다만 목표 총합과 최댓값 설정에 따라 반복 횟수가 늘어날 수 있다는 점은 참고하세요.