Thuật toán chuẩn để shuffle mảng là Fisher-Yates (Knuth Shuffle) — O(n), uniform distribution.
Ví dụ: Fisher-Yates Shuffle
<!DOCTYPE html>
<html>
<body>
<script>
function shuffleArray(array) {
// Tạo bản sao để không mutate original
const arr = [...array];
for (let i = arr.length - 1; i > 0; i--) {
// Chọn index ngẫu nhiên từ 0 đến i
const j = Math.floor(Math.random() * (i + 1));
// Hoán đổi arr[i] và arr[j]
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}
const original = [10, 20, 30, 40, 50];
const shuffled = shuffleArray(original);
document.write("Gốc: " + original + "<br>");
document.write("Shuffled: " + shuffled);
</script>
</body>
</html>
Kết quả mẫu (mỗi lần khác nhau):
Gốc: 10,20,30,40,50 Shuffled: 30,10,50,20,40
Các cách khác (KHÔNG khuyến khích):
// ❌ Sai: sort với random comparator - KHÔNG uniform!
arr.sort(() => Math.random() - 0.5); // Bias, không phân bố đều
// ❌ Sai: splice random - O(n²)
while (arr.length) {
result.push(arr.splice(Math.random() * arr.length, 1)[0]);
}
Lưu ý:
Math.random()không an toàn cho crypto — dùngcrypto.getRandomValues()nếu cần bảo mật.- Hàm trên trả về mảng mới, không thay đổi mảng gốc (immutable approach).
- Để shuffle in-place: bỏ
const arr = [...array].
Shuffle an toàn cho Crypto:
function secureShuffle(array) {
const arr = [...array];
const uint32 = new Uint32Array(1);
for (let i = arr.length - 1; i > 0; i--) {
crypto.getRandomValues(uint32);
const j = uint32[0] % (i + 1);
[arr[i], arr[j]] = [arr[j], arr[i]];
}
return arr;
}