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

Có nên dùng Array.sort() để xáo trộn (shuffle) mảng trong JavaScript không?

Không nên. Dùng array.sort(() => Math.random() - 0.5) là sai về mặt thống kê (không phân bố đều) và không ổn định trên một số engine.

Tại sao sort() random là sai?

  • Thuật toán sort (Timsort, Quicksort) giả định comparator nhất quán (transitive). Random comparator vi phạm giả định này.
  • Kết quả thiên vị: phần tử ở giữa mảng có xác suất di chuyển cao hơn.
  • Chrome V8 từng có bug khiến sort không đổi chỗ gì với random comparator.

Thuật toán đúng: Fisher-Yates (Knuth Shuffle)

function shuffleArray(array) {
  // Tạo bản sao nếu không muốn mutate original
  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]]; // Destructuring swap
  }
  return arr;
}

// Dùng
const shuffled = shuffleArray([1,2,3,4,5,6,7,8,9]);
console.log(shuffled);

Giải thích Fisher-Yates

  1. Duyệt từ cuối về đầu (i = n-1 ... 1).
  2. Chọn index ngẫu nhiên j từ 0 đến i (bao gồm i).
  3. Hoán đổi arr[i] và arr[j].
  4. Đảm bảo mọi hoán vị (permutation) có xác suất bằng nhau 1/n!.

Crypto-secure shuffle (cần bảo mật)

function secureShuffle(array) {
  const arr = [...array];
  const crypto = window.crypto || require('crypto').webcrypto;
  for (let i = arr.length - 1; i > 0; i--) {
    const randomBuffer = new Uint32Array(1);
    crypto.getRandomValues(randomBuffer);
    const j = randomBuffer[0] % (i + 1);
    [arr[i], arr[j]] = [arr[j], arr[i]];
  }
  return arr;
}