文章首发于: https://www.xiabingbao.com/post/javascript/js-random-array.html

在js中,能把数组随机打乱的方法有很多,每个方法都有自己的特点。

1. 打乱数组的方法

这里主要讲解3个打乱数组的方法。

1.1 随机从数组中取出数据

这个方法的详细操作步骤是:随机从数组中取出一个数组放入到新数组中,然后将该数据从原数组中删除,然后再随机取出下一个数,直到原数据的长度为0。

function randomArrByOut(arr) {
let result = [];
let arrTemp = [...arr]; // splice会影响原数组,复制一个新的数组,防止影响原数组
while(arrTemp.length) {
let index = Math.floor(Math.random() * arrTemp.length);
result.push(arrTemp[index]);
arrTemp.splice(index, 1);
}
return result;
}
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
randomArrByOut(arr); // [7, 1, 3, 8, 2, 4, 6, 5, 9]
randomArrByOut(arr); // [8, 4, 3, 7, 9, 2, 1, 5, 6]

这个算法看似是O(n)的算法,但实际上arr.splice内部是一个O(n^2)的算法Array.prototype.splice的内部实现:外部循环用来删除元素,内部的循环用来填充新添加的元素,或后面的元素向前移动,填充刚才被删除的元素的坑。总的算下来,这个算法的时间复杂度就是O(n^3)了。

1.2 sort方法打乱

还有一种常见的方法就是使用数组自带的sort方法来打算数组,sort方法是直接修改当前的数组:

function randomSortBySort(arr) {
arr.sort(() => Math.random() - 0.5);
}

当前环节里所有的测试均在Chrome中。当我们使用9个数据,经过多次的测试发现,打乱的数据排布并不均匀:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var n = 10000;
var count = {};
while(n--) {
randomSortBySort(arr);
var index = arr.indexOf(1); count[index] ? count[index]++ : (count[index] = 1);
}
console.log(count);
/*
数据1经过10000次打乱后的分布规律,主要集中在前2个
0: 2047
1: 1403
2: 947
3: 822
4: 777
5: 822
6: 992
7: 1008
8: 1182
*/

我们再把arr的数组扩展为15,再进行测试:

var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
var n = 10000;
var count = {};
while(n--) {
randomSortBySort(arr);
var index = arr.indexOf(1); count[index] ? count[index]++ : (count[index] = 1);
}
console.log(count);
// {0: 668, 1: 647, 2: 652, 3: 665, 4: 692, 5: 652, 6: 679, 7: 657, 8: 665, 9: 683, 10: 685, 11: 690, 12: 662, 13: 663, 14: 640}

可以发现每次打乱后的分布比较均匀,每个数字出现在每个位置的机会都是均等的!

在V8的源码中L710行中可以看到:

function InnerArraySort( array, length, comparefn ) {
// In-place QuickSort algorithm.
// For short (length <= 22) arrays, insertion sort is used for efficiency.
// 虽然注释是length<=22,但代码里是<=10 // 插入排序
var InsertionSort = function InsertionSort( a, from, to ) { }; var QuickSort = function QuickSort( a, from, to ) {
var third_index = 0;
while ( true ) {
// Insertion sort is faster for short arrays.
if ( to - from <= 10 ) {
InsertionSort( a, from, to );
return;
}
// 快排其他的内容
}
}
QuickSort(array, 0, num_non_undefined);
}

sort的内部使用快速排序,当快排拆分后的分区里的数据个数小于等于10个时,则采用插入排序!因此,当数据量比较小的时候,使用sort打乱排序时,会造成不均等的分布!

1.3 洗牌算法

最后一个经典的数组打乱算法就是洗牌算法:从最后一个数据开始往前,每次从前面随机一个位置,将两者交换,直到数组交换完毕:

function shuffleSort(arr) {
var n = arr.length; while(n--) {
var index = Math.floor(Math.random() * n);
var temp = arr[index];
arr[index] = arr[n];
arr[n] = temp;
// ES6的解耦交换方式: [arr[index], arr[n]] = [arr[n], arr[index]];
}
}

这种方式是O(n)的时间复杂度,而且还能保证一个比较均匀的分布!高效了很多

2. 从数组中随机取出多个元素

这是从数组中随机取出几个元素,上面的一节是将整个数组进行排序,而这里只是需要几个元素而已!

2.1 打乱整个数组取出数据

当然,先把整个数组打乱了,然后再取出前n个数据也是其中的一种方法,比如我们这里就使用洗牌算法打乱数组,然后取出数据:

function getRandomArr(arr, num) {
var _arr = arr.concat();
var n = _arr.length; // 先打乱数组
while(n--) {
var index = Math.floor(Math.random() * n);
[_arr[index], _arr[n]] = [_arr[n], _arr[index]];
}
return _arr.slice(0, num);
}

不过实际上我们只是需要其中的几个元素而已,如果把整个数组都打乱排序,就显得很浪费。因此这里我们使用洗牌算法的思路,稍微改进一下。

2.2 改进型

从最后一个数据开始往前,每次从前面随机一个位置,将两者交换,拿到最后的那个数据,直到达到要获取的个数:

function getRandomArr(arr, num) {
var _arr = arr.concat();
var n = _arr.length;
var result = []; // 先打乱数组
while(n-- && num--) {
var index = Math.floor(Math.random() * n); // 随机位置
[_arr[index], _arr[n]] = [_arr[n], _arr[index]]; // 交换数据
result.push(_arr[n]); // 取出当前最后的值,即刚才交换过来的值
}
return result;
}

3. 总结

数组中还是有很多的学问的,看看其中的源码,也会发现更多的奥妙!

js打乱数组的实战应用的更多相关文章

  1. JS 打乱数组顺序

    function rand(arr) { var len = arr.length //首先从最大的数开始遍历,之后递减 for(var i = arr.length - 1; i >= 0; ...

  2. 常用的sort打乱数组方法真的有用?

    JavaScript 开发中有时会遇到要将一个数组随机排序(shuffle)的需求,一个常见的写法是这样: function shuffle(arr) { arr.sort(function () { ...

  3. Js删除数组重复元素的多种方法

    js对数组元素去重有很多种处理的方法,本篇文章中为网络资源整理,当然每个方法我都去实现了:写下来的目的是希望自己活学活用,下次遇到问题后方便解决. 第一种 function oSort(arr){ v ...

  4. js去除数组重复项

    /** * js去除数组重复项 */ //方法一.使用正则法 // reg.test(str),匹配得到就返回true,匹配不到返回false var arr = ["345",& ...

  5. 005-Scala数组操作实战详解

    005-Scala数组操作实战详解 Worksheet的使用 交互式命令执行平台 记得每次要保存才会出相应的结果 数组的基本操作 数组的下标是从0开始和Tuple不同 缓冲数组ArrayBuffer( ...

  6. js 判断数组包含某值的方法 和 javascript数组扩展indexOf()方法

    var  questionId = []; var anSwerIdValue = []; ////javascript数组扩展indexOf()方法 Array.prototype.indexOf ...

  7. 探讨js字符串数组拼接的性能问题

    这篇文章主要介绍了有关js对字符串数组进行拼接的性能问题,字符串连接一直是js中性能最低的操作之一,应该如何解决呢?请参看本文的介绍 我们知道,在js中,字符串连接是性能最低的操作之一. 例如: 复制 ...

  8. js之数组,对象,类数组对象

    许久不写了,实在是不知道写点什么,正好最近有个同事问了个问题,关于数组,对象和类数组的,仔细说起来都是基础,其实都没什么好讲的,不过看到还是有很多朋友有些迷糊,这里就简单对于定义以及一下相同点,不同点 ...

  9. js对数组的操作函数

    js数组的操作 用 js有很久了,但都没有深究过js的数组形式.偶尔用用也就是简单的string.split(char).这段时间做的一个项目,用到数组的地方很多, 自以为js高手的自己居然无从下手, ...

随机推荐

  1. SSH secure shell 原理与运用

    转: http://www.ruanyifeng.com/blog/2011/12/ssh_remote_login.html 作者: 阮一峰 日期: 2011年12月21日 SSH是每一台Linux ...

  2. 【开发者笔记】插入排序过程呈现之java内置GUI表示

    先给代码,再给过程视频: package com.dyi.wyb.sort; import java.awt.Color; import java.awt.Graphics; import java. ...

  3. facebook工具xhprof的安装与使用-分析php执行性能(转载)

    下载源码包的网址 http://pecl.php.net/package/xhprof

  4. POJ1269:Intersecting Lines(判断两条直线的关系)

    题目:POJ1269 题意:给你两条直线的坐标,判断两条直线是否共线.平行.相交,若相交,求出交点. 思路:直线相交判断.如果相交求交点. 首先先判断是否共线,之后判断是否平行,如果都不是就直接求交点 ...

  5. 编辑器——sublime

    在这里只介绍自己经常使用的编辑器sublime 第一:安装node插件[出处:http://www.bubuko.com/infodetail-798008.html] 1.下载Nodejs插件,下载 ...

  6. VMware+CentOS7+jdk1.7+hadoop2.4.1

    1.工具 CentOS7:去官网下载,然后找到阿里的镜像,DVD版本就好,4个G大小https://www.centos.org/download/ vmware:去官网下载最新版本 2.要点 先装V ...

  7. Linux系统——VMware克隆

    克隆VMware 1. 关闭防火墙 2. 关闭selinux 3. 删除UUID和Mac地址 4.清空网卡缓存 5.关机 ===================== 关闭防火墙 #service ip ...

  8. APP图标设计小技巧:在iOS上快速获得APP图标的真实预览图

    严格来说,这并不是一篇关于前端开发的文章,因为涉及到的知识非常浅.这只是一个向设计狮们分享的小经验,只是其中用到了一些前端内容. 最近接了个私活,了解到一个初创公司正在高价悬赏Logo(主要用于APP ...

  9. ABP官方文档翻译 2.1 依赖注入

    依赖注入 什么是依赖注入 传统方式的问题 解决方案 构造函数注入模式 属性注入模式 依赖注入框架 ABP依赖注入基础设施 注册依赖注入 传统注册 帮助接口 自定义/直接注册 使用IocManager ...

  10. oracle在cmd下通过命令导入导出数据

    1.首先在cmd下切换到oracle的客户端的exp.exe所在的bin目录下,例如 D:\oracle\product\10.2.0\client_2\BIN 数据导出:导出的数据库名称是在tnsn ...