Nevertheless

[미니게임] Array.sort(Math.random())로 랜덤 추출하면 안 되는 이유🎰 본문

프로젝트

[미니게임] Array.sort(Math.random())로 랜덤 추출하면 안 되는 이유🎰

hxx_1 2026. 2. 5. 15:03

상식 퀴즈 부분에서 데이터 파일에 약 30개 정도의 문항을 객체 배열로 저장을 해두고, 이 배열에서 10개의 문항을 랜덤 추출을 해서 퀴즈를 구현하고자 했다.

export function getRandomQuestions(questions, count = 10) {
  return [...questions]
    .sort(() => Math.random() - 0.5)
    .slice(0, count);
}

원래의 방식은 위와 같은 알고리즘으로 하고 있었다. gpt 의 추천으로 별 생각 없이 하고 있었는데, 뭔가 프로그램을 여러 번 돌려보면 자꾸 나왔던 문제들만 계속 나오는 듯한 느낌이 있었고, 이 부분을 개선해야겠다는 생각이 들었다. 

 

🔬 문제 분석

array.sort((a, b) => Math.random() - 0.5)

이 함수는 Math.random() 으로 인해 같은 a,b 에 대해서도 호출될 때마다 다른 결과를 반환한다. 위의 코드대로 실행하면, 엔진은 두 요소를 비교할 때마다 그때그때 즉흥적으로 앞/뒤를 정해준다. 하지만 문제는, 정렬 알고리즘은 '전체 순서' 를 만들려고 한다는 점이다. 

  • sort 는 정렬 기준이 일관적이어야 한다.
  • 하지만, Math.random() 은 매번 값이 달라진다.
  • 즉, 정렬 기준이 계속 바뀌는 상태에서 정렬을 강요하게 된다. ➡️ 논리 붕괴

이로 인해, 

  • 어떤 요소는 앞쪽으로 갈 확률이 더 높아지고
  • 어떤 요소는 뒤쪽으로 밀릴 가능성이 커진다.
  • ➡️ 결과적으로 확률이 균등하지 않은 "가짜 랜덤"

 

🧪 해결 방법

export function getRandomQuestions(questions, count = 10) {
  const shuffled = [...questions];

  for (let i = shuffled.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
  }

  return shuffled.slice(0, count);
}
  • 배열의 뒤에서부터 하나씩 자리를 확정하면서, 아직 확정되지 않은 범위 내에서 랜덤 인덱스를 뽑아 swap 하는 방식
  • 각 요소는 각 위치에 갈 확률이 동일하며, 정렬 기준이 흔들리지 않는다.
  • 즉, 랜덤의 결과가 아니라 랜덤을 만드는 과정 자체가 균등하다.