๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
Frontend/JavaScript

[ ์ž๋ฐ”์Šคํฌ๋ฆฝํŠธ(JavaScript) ] shuffle ํ•จ์ˆ˜๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž

by YWTechIT 2022. 11. 5.
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
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€