前言
先看一个段代码
function randArr (arr) { return arr.sort(() => { return (Math.random() - 0.5); }); }
目的是为了实现给定数组的乱序。
利用数组的sort方法,判断随机出来的0~1的值与0.5的大小,实现伪排序。
为什么说是伪排序呢?代码的逻辑没毛病啊。
对,从这个层面来看,简单明了,完美的实现了需求,本着凡事往祖坟刨得精神。来看看这段代码的内部实现。
浏览器实现
ECMA Script
The elements of this array are sorted. The sort is not necessarily stable (that is, elements that compare equal do not necessarily remain in their original order). If comparefn is not undefined, it should be a function that accepts two arguments x and y and returns a negative value if x < y, zero if x = y, or a positive value if x > y.
大致说的意思是,我不管你排序的算法稳不稳定,反正你能给用户自定义排序规则就行,不给你就爱咋折腾咋折腾~
这帮浏览器一听,好啊,老大发话了,那就八仙过海各显神通,各自都认为自己的实现是最牛逼的。
Chrome的sort
基于V8引擎,它的排序算进行了很多的优化,但是核心是小于等于10的数组用插入排序(稳定),大于10的采用了quickSort(不稳定),源码。
FireFox的sort
基于SpiderMonkey引擎,采用了归并排序(稳定), 源码
Safari的sort
基于Nitro(JavaScriptCore )引擎,如果没有自定义的排序规则传入,采用桶排序(不一定稳定, 桶排序的稳定性取决于桶内排序的稳定性, 因此其稳定性不确定。),传入自定义规则,采用归并排序(稳定),源码
Microsoft Edge/IE9+
基于Chakra引擎,采用快排(不稳定)源码
好了,那个说sort可以不是伪排序的同学,你看见我这40米的大刀没?
什么,你还嘴硬,我喜欢你的性格,看下面:
github上的大神对 var letters = ['A','B','C','D','E',‘F’,‘G’,'H','I','J'];
进行了10000次乱序处理,发现结论: 元素大概率停留在自己的初始位置。
具体地址: HOUCe/shuffle-array
看见没,矮要承认,挨打要立正。
洗牌算法(Fisher-Yates)
想要实现真正意义上的乱序,我们来研究一下:只要满足每个元素出现在各个位置的概率同等即可。
少年,听过如来神掌吗?
有个Fisher-Yates的洗牌算法,满足您的各种乱序需求,物美价廉,杀人越货居家旅行的必备精品~(其实有三个版本,有兴趣的自行搜索)
算法的大致描述
1.找到数组的屁股(最后一个元素);
2.在脑袋和屁股中间随机一个位置;
3.交换元素;
4.这时屁股是已经乱序后的元素,所以屁股前移;
5.如果屁股没打到脑袋上就继续1~4的步骤
function shuffle(arr) { let length = arr.length, r = length - 1, rand = 0; while (r) { rand = Math.floor(Math.random() * r--); [arr[r], arr[rand]] = [arr[rand], arr[r]]; } return arr; }
参考文献
https://github.com/HOUCe/shuffle-array
https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle