關於洗牌演算法的錯誤認識

郭小昊發表於2018-05-08

下面是我之前一直使用的一個洗牌演算法:

    let arr = [1,2,3,4,5,6,7,8,9];

    Array.prototype.shuffle = function() {
      let temp = this;
      for (let i = 0; i < temp.length; i++) {
        let index       = Math.floor(Math.random()*temp.length);
        let itemAtIndex = temp[index];
        temp[index]     = temp[i]
        temp[i]         = itemAtIndex;
      }  
      return this;
    }
    console.log(arr.shuffle());

但仔細想想,其實這是非常不合理的,因為已經交換過的位置,下次仍然可能會被選上。

比較好的做法是排除已經交換過的位置,將剩下的位置洗牌,如下:

    /*
      1. 選中第一個元素,將其與n個元素中的任意一個交換(包括自己)。這時可以確定第一個元素
      2. 選中第二個元素,將其與n-1個元素中的任意一個交換(包括自己)。確定第二個元素
      3. 重複上面步驟,直到剩下一個。
      4. 該演算法事件複雜度為O(n),無偏差,各元素隨機概率相等
    */
    let arr = [1,2,3,4,5,6,7,8,9];

    Array.prototype.shuffle = function() {
      let temp = this;
      for (let i = temp.length - 1; i >= 0; i--) {
        let index       = Math.floor(Math.random()*(i+1));
        let itemAtIndex = temp[index];
        temp[index]     = temp[i]
        temp[i]         = itemAtIndex;
      }  
      return this;
    }
    console.log(arr.shuffle());

  

相關文章