Frontend/JavaScript

[ μžλ°”μŠ€ν¬λ¦½νŠΈ(JavaScript) ] shuffle ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄λ³΄μž

YWTechIT 2022. 11. 5. 16:28
728x90

πŸ“ shuffle ν•¨μˆ˜λ₯Ό λ§Œλ“€μ–΄λ³΄μž

JS둜 shuffle ν•¨μˆ˜λ₯Ό λ§Œλ“€ λ•Œ κ°€μž₯ λ¨Όμ € μƒκ°λ‚˜λŠ” λ©”μ„œλ“œλŠ” sort() 일 것이닀. μ•„λ§ˆλ„ λ‹€μŒ μ½”λ“œμ²˜λŸΌ κ΅¬μ„±ν•˜μ§€ μ•Šμ•˜μ„κΉŒ??

// sort shuffle
const arr = [1, 2, 3, 4, 5];
const shuffle = arr.sort(() => Math.random() - 0.5)

console.log(shuffle);
πŸ‘‰πŸΎ [ 5, 3, 2, 1, 4 ]

κ΅¬ν˜„ λ‘œμ§μ„ μ‚΄νŽ΄λ³΄λ©΄ 배열을 μˆœνšŒν•˜λ©΄μ„œ Math.random() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ—¬ λ‚˜μ˜¨ 랜덀 ν•œ 값에 -0.5λ₯Ό ν•˜μ—¬ sort() λ©”μ„œλ“œλ₯Ό 거치게 λœλ‹€. λ§Œμ•½, Math.random() - 0.5λ₯Ό ν•œ 값이 μ–‘μˆ˜λΌλ©΄ μ˜€λ¦„μ°¨μˆœμœΌλ‘œ μ •λ ¬λ˜κ³ , 음수λ₯Ό λ¦¬ν„΄ν•˜λ©΄ λ‚΄λ¦Όμ°¨μˆœμœΌλ‘œ μ •λ ¬λœλ‹€. arr 배열을 λͺ¨λ‘ μˆœνšŒν•˜κ²Œ 되면 상단 μ½”λ“œ 블둝 6번 Line처럼 배열이 shuffle된 것을 μ•Œ 수 μžˆλ‹€.

 

sort ν•¨μˆ˜λ‘œ κ΅¬ν˜„ν–ˆμ„ λ•Œμ˜ μž₯점은 μ½”λ“œκ°€ κ°„λ‹¨ν•΄μ§€λŠ” 반면, 단점은 λͺ¨λ“  ν™•λ₯ μ΄ κ· μΌν•˜κ²Œ μΌμ–΄λ‚˜μ§€ μ•ŠλŠ”λ‹€. (JS μ—”μ§„λ§ˆλ‹€ κ²°κ³Όκ°€ λ‹€λ₯Ό 수 μžˆλ‹€.) κ·Έ μ΄μœ λŠ” sortλŠ” 이런 μš©λ„λ‘œ λ§Œλ“€μ–΄μ§„ λ©”μ„œλ“œκ°€ μ•„λ‹ˆκΈ° λ•Œλ¬Έμ΄λ‹€. 이λ₯Ό 증λͺ…ν•˜κΈ° μœ„ν•΄ λ‹€μŒ μ½”λ“œλ₯Ό μ‚΄νŽ΄λ³΄μž. sort λ©”μ„œλ“œλ₯Ό μ΄μš©ν•΄ λ°°μ—΄ [1, 2, 3]을 백만 번 shuffle ν•΄λ΄€λ‹€.

const arr = [1, 2, 3];

function shuffle(array) {
  array.sort(() => Math.random() - 0.5);
}

let count = {
  '123': 0,
  '132': 0,
  '213': 0,
  '231': 0,
  '321': 0,
  '312': 0
};

for (let i = 0; i < 1_000_000; i++) {
  let array = [1, 2, 3];
  shuffle(array);
  count[array.join('')]++;
}

πŸ‘‰πŸΎ 
123: 375285,
132: 62554,
213: 125270,
231: 62429,
312: 62557,
321: 311905

countλ₯Ό μ‚΄νŽ΄λ³΄λ©΄ λΉˆλ„κ°€ 처음, 쀑간, 끝에 μ λ €μžˆλŠ” 것을 μ•Œ 수 μžˆλ‹€. κ·Έ μ΄μœ λŠ” sort λ©”μ„œλ“œ λ•Œλ¬ΈμΈλ°, sortλ₯Ό μ‹€ν–‰ν•˜λ©΄ 인수둜 λ„˜κΈ΄ μ •λ ¬ ν•¨μˆ˜κ°€ 배열을 μ •λ¦¬ν•΄μ£ΌλŠ”λ°, 이 κ³Όμ •μ—μ„œ element 비ꡐ가 μ™„μ „ λ¬΄μž‘μœ„λ‘œ 이루어지기 λ•Œλ¬Έμ΄λ‹€. JS μ—”μ§„λ§ˆλ‹€ λ‚΄λΆ€ κ΅¬ν˜„ 방식도 달라 이런 ν˜Όλˆμ€ 더 μ»€μ§€κ²Œ λœλ‹€.

728x90

이λ₯Ό ν•΄κ²°ν•˜κΈ° μœ„ν•΄ ν”Όμ…”-에이츠(Fisher-Yates) μ…”ν”Œμ„ μ΄μš©ν•˜λ©΄ 편ν–₯λ˜μ§€ μ•Šμ€ λΉˆλ„λ‘œ shuffle을 ν•  수 있고, μ‹œκ°„λ³΅μž‘λ„λ„ sort λ©”μ†Œλ“œλ³΄λ‹€ 이점이 μžˆλ‹€. Tim sortλ₯Ό μ‚¬μš©ν•œ sort λ©”μ†Œλ“œλŠ” 평균 O(nlogn)을 λ§Œμ‘±ν•˜μ§€λ§Œ, ν”Όμ…”-μ—μ΄μΈ λŠ” O(N)을 λ§Œμ‘±ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. ν”Όμ…”-에이츠(Fisher-Yates)의 κ΅¬ν˜„μˆœμ„œλŠ” λ‹€μŒκ³Ό κ°™λ‹€.

 

  1. 1λΆ€ν„° NκΉŒμ§€μ˜ 수 쀑 μž„μ˜μ˜ 숫자λ₯Ό μ„ νƒν•œλ‹€.
  2. 1κ³Ό μ„ νƒλ˜μ§€ μ•Šκ³  남아 μžˆλŠ” 수 μ‚¬μ΄μ˜ μž„μ˜μ˜ 수 kλ₯Ό μ„ νƒν•œλ‹€.
  3. kλ₯Ό μ„ νƒλ˜μ§€ μ•Šμ€ λ§ˆμ§€λ§‰ μˆ«μžμ™€ 자리λ₯Ό λ°”κΎΌλ‹€.
  4. 1λΆ€ν„° N-1κΉŒμ§€μ˜ 수 쀑 μž„μ˜μ˜ 숫자λ₯Ό μ„ νƒν•œλ‹€.
  5. 더 이상 선택할 μˆ«μžκ°€ 없을 λ•ŒκΉŒμ§€ 2~4번의 과정을 λ°˜λ³΅ν•œλ‹€.

 

 

이λ₯Ό JSμ½”λ“œλ‘œ μž‘μ„±ν•˜λ©΄ λ‹€μŒκ³Ό κ°™λ‹€.

// Fisher-Yates shuffle
function shuffle(array) {
  for (let i = array.length - 1; i > 0; i--) {  // λ°°μ—΄μ˜ λ§ˆμ§€λ§‰λΆ€ν„° μ‹œμž‘ν•œλ‹€. iκ°€ 0이되면 λ°˜λ³΅λ¬Έμ„ μ’…λ£Œν•œλ‹€.
    let j = Math.floor(Math.random() * (i + 1));  // λžœλ€ν•œ κ°’κ³Ό ν˜„μž¬ 인덱슀 μœ„μΉ˜ + 1을 κ³±ν•˜κ³  μ†Œμˆ˜μ μ„ μ œκ±°ν•œλ‹€.
    [array[i], array[j]] = [array[j], array[i]];  // ν˜„μž¬ μΈλ±μŠ€μ™€ λžœλ€ν•œκ°’μ— 인덱슀 μœ„μΉ˜ + 1을 κ³±ν•˜κ³  μ†Œμˆ˜μ μ„ μ œκ±°ν•œ κ°’κ³Ό 자리λ₯Ό λ°”κΎΌλ‹€.
  }
}

νƒ€μž…μŠ€ν¬λ¦½νŠΈλ‘œ μ½”λ“œλ₯Ό 가독성 있게 μž‘μ„±ν•΄λ΄€λ‹€. 그리고 백만 번 shuffle ν•΄λ΄€κ³  결둠적으둜 ν™•λ₯ μ΄ κ· μΌν•˜κ²Œ λ‚˜μ˜€λŠ” 것을 μ•Œ 수 μžˆλ‹€.

function shuffle<T>(array: T[]) {
  for (let index = array.length - 1; index > 0; index--) {
    const randomIndex = Math.floor(Math.random() * (index + 1))

    [array[index], array[randomIndex]] = [array[randomIndex], array[index]]
  }
  return array
}

let count = {
  '123': 0,
  '132': 0,
  '213': 0,
  '231': 0,
  '321': 0,
  '312': 0
};

for (let i = 0; i < 1000000; i++) {
  let array = [1, 2, 3];
  shuffle(array);
  count[array.join('')]++;
}

πŸ‘‰πŸΎ
123: 166693
132: 166647
213: 166628
231: 167517
312: 166199
321: 166316

μ΄λ ‡κ²Œ shuffle ν•¨μˆ˜λ₯Ό μ•Œμ•„λ΄€λŠ”λ°, λ°μ΄ν„°μ˜ 크기가 μž‘κ³  λΉˆλ„μ— 상관없이 κ°„νŽΈν•˜κ²Œ shuffle을 κ΅¬ν˜„ν•˜λ €λ©΄ sortλ₯Ό μ‚¬μš©ν•΄λ„ λ˜μ§€λ§Œ, λ°μ΄ν„°μ˜ 크기가 크고 λ°œμƒ λΉˆλ„κ°€ κ· μΌν•˜κ²Œ μΌμ–΄λ‚˜μ•Ό ν•œλ‹€λ©΄ ν”Όμ…”-μ—μ΄μΈ μ˜ λ‘œμ§μ„ μ‚¬μš©ν•˜λŠ” 것이 μ–΄λ–¨κΉŒ?라고 μƒκ°ν•˜λ©΄μ„œ 글을 λ§ˆμΉœλ‹€.

 

Reference

  1. ν”Όμ…”-에이츠 μ…”ν”Œ
  2. Fisher–Yates Shuffle - Mike Bostock
  3. How to shuffle an array in JavaScript - YouTube
λ°˜μ‘ν˜•