JavaScript数组随机排序的多种实现与洗牌算法详解

作者:拾光时间:2026-10-29 23:45:06浏览:381

本文深入解析JavaScript中数组随机排序的多种实现方式。从利用sort结合Math.random的基础方法,到解决分布不均问题的递归算法,再到经典的Fisher-Yates洗牌算法及underscore.js实现。详细对比各方案原理、代码示例及性能特点,帮助开发者根据实际需求选择最优的数组乱序方案。

推荐阅读:JavaScript学习笔记之数组求和方法

JavaScript学习笔记之数组的增、删、改、查

基于sort与Math.random的基础排序

JavaScript内置了sort()和reverse()方法用于数组重排,但在实际业务中,如扑克牌游戏的随机洗牌场景,这些方法往往无法直接满足需求。

基于sort与Math.random的基础排序

本文将探讨如何实现上述随机洗牌效果,并梳理数组随机排序的相关技术知识。

查阅相关资料发现,Math.random()是实现数组随机排序的核心工具。在浏览器控制台输入:

Math.random()

基于sort与Math.random的基础排序

结果显示Math.random()生成0到1之间的随机数。sort()方法接受一个比较函数作为参数,若返回-1则a排在b前。据此可构造随机比较函数:将Math.random()的结果与0.5比较,大于0.5时返回-1(a在前),否则返回1(b在前)。


function randomSort(a, b) {
return Math.random() > 0.5 ? -1 : 1;
}

参考示例:


var arr = [1,2,3,4,5,6,7,8,9];
arr.sort(randomSort);

基于sort与Math.random的基础排序

由此可实现文章开头的随机排序效果:

尽管上述方法实现了随机排序,但元素在新数组中的位置分布并非完全均匀。例如,原数组中值为1的元素初始索引为0,随机排序后其出现在0-8各位置的概率理论上应相等,但实际因sort()的依次比较机制导致分布呈递减趋势。

递归方法处理分布不均问题

针对此分布不均问题,可采用递归方法进行处理:


function randomSort(arr, newArr) {
// 如果原数组arr的length值等于1时,原数组只有一个值,其键值为0
// 同时将这个值push到新数组newArr中
if(arr.length == 1) {
newArr.push(arr[0]);
return newArr; // 相当于递归退出
}
// 在原数组length基础上取出一个随机数
var random = Math.ceil(Math.random() * arr.length) - 1;
// 将原数组中的随机一个值push到新数组newArr中
newArr.push(arr[random]);
// 对应删除原数组arr的对应数组项
arr.splice(random,1);
return randomSort(arr, newArr);
}

使用方式如下:


for (var i = 0; i < 10; i++) {
var arr=[1,2,3,4,5,6,7,8,9];
var newArr=[];
randomSort(arr,newArr);
console.log(newArr);
}

输出结果:

基于sort与Math.random的基础排序

执行randomSort(arr,newArr)函数后,原数组arr将被清空。

若用此方法实现开头的洗牌示例,需在resetPic()函数中重置pukePic数组:

其他常见随机排序实现方案

除上述两种方法外,@Traveller在DIV.IO分享的《数组元素随机化排序算法实现》一文提供了三种实现思路:

利用数组sort方法对数组元素随机排序


Array.prototype.shuffle = function(n) {
var len = this.length ,
num = n ? Math.min(n,len) : len,
arr = this.slice(0);
arr.sort(function(a,b){
return Math.random()-0.5;
});
return arr.slice(0,num-1);
}

随机交换数组内的元素


lib = {}
lib.range = function(min,max) {
return min + Math.floor(Math.random()*(max-min+1));
}

Array.prototype.shuffle = function(n) {
var len = this.length,
num = n ? Math.min(n,len) : len,
arr = this.slice(0),
temp,
index;
for (var i=0;i<len;i++){
index = lib.range(i,len-1);
temp = arr[i];
arr[i] = arr[index];
arr[index]=temp;
}
return arr.slice(0,num);
}

从原数组随机抽取元素加入新数组


lib = {}
lib.range = function(min,max) {
return min+Math.floor(Math.random()*(max-min+1));
}
Array.prototype.shuffle = function(n) {
var len = this.length, 
num = n ? Math.min(n,len) : len,
arr = this.slice(0),
result=[],
index;
for (var i=0;i<num;i++){
index = lib.range(0,len-1-i);
// 或者 result.concat(arr.splice(index,1))
result.push(arr.splice(index,1)[0]);
}
return result
}

Fisher-Yates洗牌算法原理与实现

洗牌算法

数组随机排序的核心原理是洗牌算法(Fisher–Yates shuffle):

这是一种将有限集合顺序打乱的算法。

原理

定义长度为原数组长度length的数组shuffled。取0到index(初始为0)的随机值rand,执行shuffled[index] = shuffled[rand],shuffled[rand] = arr[index]。index递增,重复直至index等于length-1。该过程即为shuffled从0到length-1的赋值,新加入值为arr[index],shuffled[index]赋值为已赋值元素中的随机值shuffled[rand],因存在重复值,故将shuffled[rand]重新赋值为arr[index]。

underscore.js中的shuffle方法


function random(min, max) {
if (max == null) {
max = min;
min = 0;
}
return min + Math.floor(Math.random() * (max - min + 1));
};
function shuffle(arr) {
var length = arr.length,
shuffled = Array(length);
for (var index = 0, rand; index < length; index++) {
rand = random(0, index);
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = arr[index];
}
return shuffled;
}

实际应用示例:


var arr = [1,2,3,4,5,6,7,8,9];
for (var i = 0; i < 10; i++){
console.log(shuffle(arr));
}

Chrome浏览器输出结果如下:

基于sort与Math.random的基础排序

同样使用洗牌算法完成文章开头的示例:

基于sort与Math.random的基础排序

此外还有更简洁易读的写法:


function shuffle(arr) {
var i, 
j,
temp;
for (i = arr.length - 1; i > 0; i--) {
j = Math.floor(Math.random() * (i + 1));
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
return arr;
};

总结

本文汇总了数组随机排序的相关资料。实现类似功能的方法众多,此处仅整理部分常见方案。如有更优方法,欢迎在评论区分享。

以上为JavaScript学习笔记之数组随机排序的介绍,希望能对大家有所帮助!

作者最新文章