Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Cách xáo trộn (shuffle) mảng JavaScript ngẫu nhiên?

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ùng crypto.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;
}