อัลกอริทึม Fisher-Yates (Knuth Shuffle)
วิธีมาตรฐานในการสุ่มลำดับ Array ให้ได้การกระจายที่เท่าเทียม (Uniform Distribution) คือ Fisher-Yates Shuffle
ตัวอย่างโค้ด
<html>
<body>
<script>
function randomFunc(myArr) {
var l = myArr.length, temp, index;
while (l > 0) {
index = Math.floor(Math.random() * l);
l--;
temp = myArr[l];
myArr[l] = myArr[index];
myArr[index] = temp;
}
return myArr;
}
var arr = [10, 20, 30, 40, 50];
document.write(randomFunc(arr));
</script>
</body>
</html>
ผลลัพธ์ตัวอย่าง
20,50,40,30,10
หมายเหตุ: ผลลัพธ์จะต่างกันทุกครั้งที่รัน
คำอธิบายอัลกอริทึม
- เริ่มจาก index สุดท้าย (
l = length - 1) - สุ่ม index ระหว่าง
0ถึงl(Inclusive) - สลับค่าระหว่าง
myArr[l]และmyArr[index] - ลด
lลง 1 ทำซ้ำจนl = 0
เวอร์ชัน ES6+ แบบสั้น (Immutable - ไม่เปลี่ยน Array ต้นฉบับ)
function shuffle(array) {
return [...array].sort(() => Math.random() - 0.5);
}
// หมายเหตุ: sort() แบบนี้ไม่ได้ Uniform Distribution จริง แต่ใช้ได้สำหรับงานเบาๆ
// Fisher-Yates แบบ Immutable
function shuffleImmutable(array) {
const arr = [...array];
for (let i = arr.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
ตัวอย่างการใช้งานจริง
// สุ่มการ์ด const deck = [...Array(52)].map((_, i) => i); const shuffled = shuffleImmutable(deck); // สุ่มผู้ชนะ const participants = ['Alice', 'Bob', 'Charlie', 'David']; const winner = shuffleImmutable(participants)[0]; // สุ่มลำดับเพลง const playlist = ['song1.mp3', 'song2.mp3', 'song3.mp3']; player.playlist = shuffleImmutable(playlist);
ข้อควรระวัง
array.sort(() => Math.random() - 0.5)ไม่ได้เป็น Uniform Shuffle เนื่องจาก Sort Algorithm ไม่ได้ออกแบบมาเพื่อสุ่ม- Fisher-Yates ให้ผลลัพธ์ Uniform จริงๆ และมี Performance ดี (O(n))
- ฟังก์ชันตัวอย่าง
randomFuncแบบเดิม เปลี่ยน Array ต้นฉบับ (Mutate) ควรสร้าง Copy ก่อนถ้าต้องการเก็บต้นฉบับ