通过js随机函数Math.random实现乱序


Posted in Javascript onMay 19, 2020

乱序的意思想必没有不知道:就是将数组打乱。听到乱序一般都会想到js的随机函数Math.random();

var values = [1, 2, 3, 4, 5];
values.sort(function() {
  return Math.random() - 0.5;
});
console.log(values)

利用数组的sort方法,判断随机出来的0~1值与0.5的大小,实现排序。看似一个很不错的方案,代码逻辑也没毛病,一般情况下也确实能够做到乱序。但是,这是一个伪排序,是的还有但是(我也是今天才知道的,不求甚解的毛病啊~),为什么呢?先看看这个乱序的结果吧:

var times = [0, 0, 0, 0, 0];
for (var i = 0; i < 100000; i++) {
  let arr = [1, 2, 3, 4, 5];
  arr.sort(() => Math.random() - 0.5);
  times[arr[4] - 1]++;
};
console.log(times)

测试的原理是:将[1, 2, 3, 4, 5]乱序10万次,计算乱序后数组的最后一个元素是1,2,3,4,5的次数分别是多少。

运行几次得到的结果为:

通过js随机函数Math.random实现乱序通过js随机函数Math.random实现乱序通过js随机函数Math.random实现乱序

由这几次运行得到的结果可以看出:2出现的最后的次数明显少于其他数字,不是随机吗?按理说概率应该是相差不多才对啊!
其实问题是在sort方法,各个浏览器对sort的实现方式不一样。

Chrome的sort

基于V8引擎,它的排序算进行了很多的优化,但是核心是小于等于10的数组用插入排序(稳定),大于10的采用了quickSort(不稳定)

FireFox的sort

基于SpiderMonkey引擎,采用了归并排序(稳定)

Safari的sort

基于Nitro(JavaScriptCore )引擎,如果没有自定义的排序规则传入,采用桶排序(不一定稳定, 桶排序的稳定性取决于桶内排序的稳定性, 因此其稳定性不确定。),传入自定义规则,采用归并排序(稳定)

Microsoft Edge/IE9+

基于Chakra引擎,采用快排(不稳定)

以下用chrome测试乱序各种结果的概率:

var times = 100000;
var res = {};
for(var i = 0; i < times; i++){
  var arr = [1, 2, 3];
  arr.sort(() => Match.random() - 0.5);
  var key = JSON.stringify(arr);
  res[key] ? res[key]++ : res[key] = 1;
}

// 为了方便展示,转换成百分比
for (var key in res) {
  res[key] = res[key] / times * 100 + '%';
}
console.log(res);

结果如下

通过js随机函数Math.random实现乱序

几种结果出现的概率相差很大...所以说不是一个真正的乱序。

Fisher-Yates算法【也叫“洗牌算法”】:为什么叫 Fisher?Yates 呢? 因为这个算法是由 Ronald Fisher 和 Frank Yates 首次提出的。代码如下:

function shuffle(a) {
  var j, x, i;
  for (i = a.length; i; i--) {
    j = Math.floor(Math.random() * i);
    x = a[i-1];
    a[i - 1] = a[j];
    a[j] = x;
  }
  return a;
}

其原理就是:遍历数组元素,然后将当前元素与以后随机位置的元素进行交换,这样乱序更加彻底。

如果用ES6的写法还能精简成:

function shuffle(a) {
  for(let i = a.length; i; i--) {
    let j = Math.floor(Math.random() * i);
    [a[i - 1], a[j]] = [a[j], a[i - 1]];
  }
  return a;
}

再用上面的demo测试一下:

var times = 100000;
var res = {};

for (var i = 0; i < times; i++) {
  var arr = shuffle([1, 2, 3]);

  var key = JSON.stringify(arr);
  res[key] ? res[key]++ : res[key] = 1;
}

// 为了方便展示,转换成百分比
for (var key in res) {
  res[key] = res[key] / times * 100 + '%'
}

console.log(res)

得到结果如下:

通过js随机函数Math.random实现乱序

各种结果的概率都基本相同了,所以真正实现了乱序的效果!

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
通过隐藏option实现select的联动效果
Nov 10 Javascript
jQuery.validate 常用方法及需要注意的问题
Mar 20 Javascript
jquery查找tr td 示例模拟
May 08 Javascript
Boostrap入门准备之border box
May 09 Javascript
Angular2 (RC4) 路由与导航详解
Sep 21 Javascript
通过bootstrap全面学习less
Nov 09 Javascript
webpack打包并将文件加载到指定的位置方法
Feb 22 Javascript
解决vue v-for 遍历循环时key值报错的问题
Sep 06 Javascript
详解JavaScript的变量
Apr 04 Javascript
vue中使用vue-cli接入融云实现即时通信
Apr 19 Javascript
js的新生代垃圾回收知识点总结
Aug 22 Javascript
Javascript设计模式之原型模式详细
Oct 05 Javascript
javascript实现获取中文汉字拼音首字母
May 19 #Javascript
微信小程序实现菜单左右联动
May 19 #Javascript
JS array数组检测方式解析
May 19 #Javascript
基于JS实现table导出Excel并保留样式
May 19 #Javascript
vue利用全局导航守卫作登录后跳转到未登录前指定页面的实例代码
May 19 #Javascript
jQuery 函数实例分析【函数声明、函数表达式、匿名函数等】
May 19 #jQuery
JS加载解析Markdown文档过程详解
May 19 #Javascript
You might like
使用bcompiler对PHP文件进行加密的代码
2010/08/29 PHP
Codeigniter整合Tank Auth权限类库详解
2014/06/12 PHP
PHP将Excel导入数据库及数据库数据导出至Excel的方法
2015/06/24 PHP
PHP保存Base64图片base64_decode的问题整理
2019/11/04 PHP
PHP全局使用Laravel辅助函数dd
2019/12/26 PHP
PHP+Redis事务解决高并发下商品超卖问题(推荐)
2020/08/03 PHP
ThinkPHP 5 AJAX跨域请求头设置实现过程解析
2020/10/28 PHP
JavaScript 面向对象的 私有成员和公开成员
2010/05/13 Javascript
firefox下frameset取不到值的解决方法
2010/09/06 Javascript
页面装载js及性能分析方法介绍
2014/03/21 Javascript
jquery 3D 标签云示例代码
2014/06/12 Javascript
jQuery实现自定义checkbox和radio样式
2015/07/13 Javascript
jQuery实现点击某个div打开层,点击其他div关闭层实例分析(阻止冒泡)
2016/11/18 Javascript
基于hover的用法实例(推荐)
2017/07/04 Javascript
jQuery实现的form转json经典示例
2017/10/10 jQuery
Nodejs实现爬虫抓取数据实例解析
2018/07/05 NodeJs
跨域解决之JSONP和CORS的详细介绍
2018/11/21 Javascript
mock.js模拟前后台交互
2019/07/25 Javascript
JavaScript实现图片轮播特效
2019/10/23 Javascript
vue2.x 对象劫持的原理实现
2020/04/19 Javascript
Ubuntu安装Jupyter Notebook教程
2017/10/18 Python
python实现决策树分类算法
2017/12/21 Python
Python实现钉钉发送报警消息的方法
2019/02/20 Python
Django 配置多站点多域名的实现步骤
2019/05/17 Python
在Python中过滤Windows文件名中的非法字符方法
2019/06/10 Python
Python学习工具jupyter notebook安装及用法解析
2020/10/23 Python
Python使用Pygame绘制时钟
2020/11/29 Python
CSS3 实现侧边栏展开收起动画
2014/12/22 HTML / CSS
HTML5中的websocket实现直播功能
2018/05/21 HTML / CSS
京东奢侈品:全球奢侈品牌
2018/03/17 全球购物
同居协议书范本
2014/04/23 职场文书
2014年反洗钱工作总结
2014/11/22 职场文书
幼儿园春季开学通知
2015/07/16 职场文书
2016年秋季新学期致辞
2015/07/30 职场文书
党校培训学习心得体会
2016/01/06 职场文书
python元组打包和解包过程详解
2021/08/02 Python