关于数组乱序的深挖——“感觉一直在写毒代码”
最近看了一篇非常有趣的文章:关于JavaScript的数组随机排序,其作者为oldj前辈。文中指出我们用来“将一个数组随机排序”的经典写法所存在的问题,获益匪浅。
本文将以更加详尽的材料和更多样的code demo进行阐述。并尝试用“Fisher–Yates shuffle”洗牌算法进行终极解答。
多个熟悉的场景
将一个数组进行乱序处理,是一个非常简单但是非常常用的需求。
比如,“猜你喜欢”、“点击换一批”、“中奖方案”等等,都可能应用到这样的处理。包括我自己在写代码的时候,也确实遇到过。
一般比较经典且流行的方案为:对对象数组采用array.sort()方法,并传入一个比较函数(comparison function),这个比较函数随机返回一个介于[-0.5, 0.5]之间的数值:
var numbers = [12,4,16,3];
numbers.sort(function() {
return .5 - Math.random();
});
关于这么做的理论基础这里不再进行阐释。如果您不明白,可以了解一下JS中sort函数的使用方法。
有毒的array.sort方法
正像oldj前辈文章指出的那样,其实使用这个方法乱序一个数组是有问题的。
为此,我写了一个脚本进行验证。并进行了可视化处理。强烈建议读者去Github围观一下,clone下来自己试验。
脚本中,我对
var letters = ['A','B','C','D','E','F','G','H','I','J'];
letters这样一个数组使用array.sort方法进行了10000次乱序处理,并把乱序的每一次结果存储在countings当中。
结果在页面上进行输出:
var countings = [
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0},
{A:0,B:0,C:0,D:0,E:0,F:0,G:0,H:0,I:0,J:0}
];
var letters=['A','B','C','D','E','F','G','H','I','J'];
for (var i = 0; i < 10000; i++) {
var r = ['A','B','C','D','E','F','G','H','I','J'].sort(function() {
return .5 - Math.random();
});
for(var j = 0; j <= 9; j++) {
countings[j][r[j]]++;
}
}
for(var i = 0; i <= 9;i++) {
for(var j = 0;j <=