1. 程式人生 > 程式設計 >通過js隨機函式Math.random實現亂序

通過js隨機函式Math.random實現亂序

亂序的意思想必沒有不知道:就是將陣列打亂。聽到亂序一般都會想到js的隨機函式Math.random();

var values = [1,2,3,4,5];
values.sort(function() {
  return Math.random() - 0.5;
});
console.log(values)

利用陣列的sort方法,判斷隨機出來的0~1值與0.5的大小,實現排序。看似一個很不錯的方案,程式碼邏輯也沒毛病,一般情況下也確實能夠做到亂序。但是,這是一個偽排序,是的還有但是(我也是今天才知道的,不求甚解的毛病啊~),為什麼呢?先看看這個亂序的結果吧:

var times = [0,0];
for (var i = 0; i < 100000; i++) {
  let arr = [1,5];
  arr.sort(() => Math.random() - 0.5);
  times[arr[4] - 1]++;
};
console.log(times)

測試的原理是:將[1,5]亂序10萬次,計算亂序後陣列的最後一個元素是1,5的次數分別是多少。

執行幾次得到的結果為:

通過js隨機函式Math.random實現亂序通過js隨機函式Math.random實現亂序通過js隨機函式Math.random實現亂序

由這幾次執行得到的結果可以看出:2出現的最後的次數明顯少於其他數字,不是隨機嗎?按理說概率應該是相差不多才對啊!
其實問題是在sort方法,各個瀏覽器對sort的實現方式不一樣。

Chrome的sort

基於V8引擎,它的排序算進行了很多的優化,但是核心是小於等於10的陣列用插入排序(穩定),大於10的採用了quickSort(不穩定)

FireFox的sort

基於SpiderMonkey引擎,採用了歸併排序(穩定)

Safari的sort

基於Nitro(JavaScriptCore )引擎,如果沒有自定義的排序規則傳入,採用桶排序(不一定穩定, 桶排序的穩定性取決於桶內排序的穩定性,因此其穩定性不確定。),傳入自定義規則,採用歸併排序(穩定)

Microsoft Edge/IE9+

基於Chakra引擎,採用快排(不穩定)

以下用chrome測試亂序各種結果的概率:

var times = 100000;
var res = {};
for(var i = 0; i < times; i++){
  var arr = [1,3];
  arr.sort(() => Match.random() - 0.5);
  var key = JSON.stringify(arr);
  res[key] ? res[key]++ : res[key] = 1;
}

// 為了方便展示,轉換成百分比
for (var key in res) {
  res[key] = res[key] / times * 100 + '%';
}
console.log(res);

結果如下

通過js隨機函式Math.random實現亂序

幾種結果出現的概率相差很大...所以說不是一個真正的亂序。

Fisher-Yates演算法【也叫“洗牌演算法”】:為什麼叫 Fisher–Yates 呢? 因為這個演算法是由 Ronald Fisher 和 Frank Yates 首次提出的。程式碼如下:

function shuffle(a) {
  var j,x,i;
  for (i = a.length; i; i--) {
    j = Math.floor(Math.random() * i);
    x = a[i-1];
    a[i - 1] = a[j];
    a[j] = x;
  }
  return a;
}

其原理就是:遍歷陣列元素,然後將當前元素與以後隨機位置的元素進行交換,這樣亂序更加徹底。

如果用ES6的寫法還能精簡成:

function shuffle(a) {
  for(let i = a.length; i; i--) {
    let j = Math.floor(Math.random() * i);
    [a[i - 1],a[j]] = [a[j],a[i - 1]];
  }
  return a;
}

再用上面的demo測試一下:

var times = 100000;
var res = {};

for (var i = 0; i < times; i++) {
  var arr = shuffle([1,3]);

  var key = JSON.stringify(arr);
  res[key] ? res[key]++ : res[key] = 1;
}

// 為了方便展示,轉換成百分比
for (var key in res) {
  res[key] = res[key] / times * 100 + '%'
}

console.log(res)

得到結果如下:

通過js隨機函式Math.random實現亂序

各種結果的概率都基本相同了,所以真正實現了亂序的效果!

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。