这个_.shuffle实现背后的逻辑是什么?

时间:2017-01-20 10:22:53

标签: javascript underscore.js

有人可以告诉我这个实现变体的工作原理吗?

我对如何确保 var rand 生成的随机数(也就是我们试图访问的索引)是唯一的以确保我们进行洗牌感到困惑给定的数组。

EG。给定[1,2,3,4]我们返回[3,4,2,1]而不是[3,3,4,4],因为生成的随机数恰好重复了2和3的索引?

  _.shuffle = function(array) {
    var newArr = array.slice();
      for (var i=array.length-1; i>0; i--) {
       var rand = Math.round(Math.random() * i);
       var temp = newArr[rand];
       newArr[rand] = newArr[i];
       newArr[i] = temp;
       }
   return newArr;
  };

4 个答案:

答案 0 :(得分:1)

ran不一定是唯一的。

对于for循环的每次迭代,混洗算法将交换数组中的两个项目。

因此,如果ran不是唯一的,那就没问题,因为我们可以将两个项重新交换多次,而不会导致问题。

另外,请注意该数组已发生变异。因此,对于循环的每次迭代,我们操作数组的最新状态。这意味着如果我们多次交换相同的索引,我们就不会创建重复项。

答案 1 :(得分:0)

这叫做Fisher-Yates shuffle。维基百科有一篇相当精细的文章:https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle

答案 2 :(得分:0)

阅读annotated version of the source

  

使用Fisher-Yates shuffle的现代版本随机播放集合。

不,rand不是唯一生成的,它可以是不同迭代中的相同数字。但是通过算法的设计,它总是指向一个非混洗元素(在[0, i]范围内)。

答案 3 :(得分:0)

这是处理随机值的错误实现的一个很好的例子。

使用Math.round,您得到的结果并不是真正相等的分布式,这意味着在整个范围的开始和结束时,您只需获得一半的值。

var rand = Math.round(Math.random() * i);
//              ^^^^^



var _ = {},
    i,
    array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    count = { };

_.shuffle = function (array) {
    var newArr = array.slice();
    for (var i = array.length - 1; i > 0; i--) {
        var rand = Math.round(Math.random() * i);
        var temp = newArr[rand];
        newArr[rand] = newArr[i];
        newArr[i] = temp;
    }
    return newArr;
};

array.forEach(function (a) {
    count[a] = count[a] || [];
});
for (i = 0; i < 100000; i++) {
    _.shuffle(array).forEach(function (a, i) {
        count[a][i] = (count[a][i] || 0) + 1;
    });
}
console.log(count);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;

使用

分发的示例
var rand = Math.floor(Math.random() * (i + 1));
//              ^^^^^                    ^^^

&#13;
&#13;
var _ = {},
    i,
    array = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'],
    count = { };

_.shuffle = function (array) {
    var newArr = array.slice();
    for (var i = array.length - 1; i > 0; i--) {
        var rand = Math.floor(Math.random() * (i + 1));
        var temp = newArr[rand];
        newArr[rand] = newArr[i];
        newArr[i] = temp;
    }
    return newArr;
};

array.forEach(function (a) {
    count[a] = count[a] || [];
});
for (i = 0; i < 100000; i++) {
    _.shuffle(array).forEach(function (a, i) {
        count[a][i] = (count[a][i] || 0) + 1;
    });
}
console.log(count);
&#13;
.as-console-wrapper { max-height: 100% !important; top: 0; }
&#13;
&#13;
&#13;