博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
洗牌算法具体指的是什么?
阅读量:6181 次
发布时间:2019-06-21

本文共 2955 字,大约阅读时间需要 9 分钟。

这里是修真院前端小课堂,每篇分享文从

【背景介绍】【知识剖析】【常见问题】【解决方案】【编码实战】【扩展思考】【更多讨论】【参考文献】

八个方面深度解析前端知识/技能,本篇分享的是:

【洗牌算法具体指的是什么?  】

1)背景介绍:

为了让游戏更加公平,我们希望洗牌时得到一个随机的排序,一个简单有效的办法就是把牌随

机选一叠放

另外一边做一个新的牌堆,并且重复这一步。只要从剩余牌堆中选出来的牌的概率是相等的,

就会得到公平的牌堆。

 

相似的,对于数组【0,n】,如何对它随机排列元素呢

 

(2)知识剖析:

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

function shuffle(arr) {

arr.sort(function () {

return Math.random() - 0.5;

});

}

 

或者使用更简洁的 ES6 的写法(箭头函数创建更简短的函数和不引入this):

function shuffle(arr) {

arr.sort(() => Math.random() - 0.5);

}

 

(3)常见问题:

但是这种写法有问题,并不能实现真正的随机

这样排序的问题

 

看下面的代码,我们生成一个长度为 10 的数组['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',

'j'],使用上面的方法将数组乱序,执行多次后,会发现每个元素仍然有很大机率在它原来的位置附近出现。

 

let n = 10000;

let count = (new Array(10)).fill(0);

for (let i = 0; i < n; i ++) {

let arr = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'];

arr.sort(() => Math.random() - 0.5);

count[arr.indexOf('a')]++;

}

console.log(count);

 

如果排序真的是随机的,那么每个元素在每个位置出现的概率都应该一样,

但是这种排序各个位置的字母会在原位置的左右徘徊。

因此,我们可以认为,使用形如arr.sort(() => Math.random() - 0.5)这样的方法得到的并不是真正的随机排序。

 

浏览器打印台:(10) [2892, 2939, 1968, 1040, 582, 288, 161, 66, 44, 20]

 

国外有人写了一个Shuffle算法可视化的页面,

                    在上面可以更直观地看到使用arr.sort(() => Math.random() - 0.5)的确是很不随机的。

 

 

(4)解决方案:

Fisher–Yates shuffle算法

这个算法由 Ronald Fisher 和 Frank Yates 于 1938 年提出,然后在 1964 年由 Richard Durstenfeld 改编为适用于电脑编程的版本。

 

(5)编码实战:

ES6实现方法:

function shuffle(arr) {

let i = arr.length;

while (i) {

let j = Math.floor(Math.random() * i--);

[arr[j], arr[i]] = [arr[i], arr[j]];

}

}

 

ES5实现:

function shuffle(arr) {

var i = arr.length, t, j;

while (i) {

j = Math.floor(Math.random() * i--);

t = arr[i];

arr[i] = arr[j];

arr[j] = t;

}

}

 

比较清晰的一种写法

Array.prototype.shuffle = function () {

var input = this;

for (var i = input.length - 1; i >= 0; i--) {

var randomIndex = Math.floor(Math.random() * (i + 1));

var itemAtIndex = input[randomIndex];

input[randomIndex] = input[i];

input[i] = itemAtIndex;

}

return input;

}

var tempArray = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] ;

tempArray.shuffle(); // and the result is... alert(tempArray);

prototype 属性是在函数作为构造器使用的时候,作为其构造对象的原型

this 引用的就是调用该 shuffle 的数组:

for循环遍历数组,顺序是从后往前,从input.length-1位置开始,直到第一个元素

var randomIndex = Math.floor(Math.random() * (i + 1));存储一个随机数,用作数组的索引,来提取一个随机元素,最大值是i的值

var itemAtIndex = input[randomIndex];用新变量保存该随机元素值,比如随机生产的索引2,那么该随机值就是3

最后两行是把选中的元素和随机的元素交换一下

 

(6)拓展思考:

除了洗牌算法,类似的,还有哪些对数组的排序方式?

冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。

走访数列的工作是重复地进行直到没有再需要交换,

也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

 

比较相邻的元素。如果第一个比第二个大,就交换它们两个;

对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;

针对所有的元素重复以上的步骤,除了最后一个;

重复步骤1~3,直到排序完成。

 

(7)参考文献:

参考一:

 

(8)更多讨论:

提问1:

除了Fisher–Yates shuffle算法还有哪些随机算法?

回答:比如上面提到的arr.sort(() => Math.random() - 0.5)算法就是一个常用的方法。尽管得到的并不是完全的随机

提问2:洗牌算法的实际运用?

回答:比如听歌软件的随便听听功能

 

提问3:fisher算法比sort算法究竟好在哪里

回答:

再拿抽牌举例,因为fisher利用了抽卡本身的顺序,保证到了每一张原本序列中的卡,而sort方法抽取存在出现重复位置的可能性,就等于浪费了一次排序的机会,换句话说,其等效抽卡次数因为出现了过去相同的洗法,有效洗牌次数下降,样本空间缩小,无法充满整个n!空间,所以有效性会下降。而Fisher–Yates算法在原理上保证了不会出现浪费次数,重复选择的情况,导致样本空间一直保持n!,没有坍缩,这就是其在数学意义上优秀的原因。

转载地址:http://yddda.baihongyu.com/

你可能感兴趣的文章
Monte Carlo tree search 学习
查看>>
使用golang的slice来模拟栈
查看>>
【计算机网络】TCP关闭连接问题及注意
查看>>
【评分】第四次作业--项目选题报告(团队)
查看>>
增加wamp64 PHP支持版本
查看>>
重复枚举和不重复枚举
查看>>
ES正常停止步骤
查看>>
通配符的匹配很全面, 但无法找到元素 'context:component-scan' 的声明。
查看>>
Kafka的CommitFailedException异常
查看>>
思考与阅读
查看>>
ES6
查看>>
Wireshark中的一些SNMP相关的过滤器
查看>>
java8 新特性
查看>>
Xilinx Vivado的使用详细介绍(1):创建工程、编写代码、行为仿真、Testbench
查看>>
在 Scale Up 中使用 Health Check - 每天5分钟玩转 Docker 容器技术(145)
查看>>
基于 HTML5 Canvas 实现的文字动画特效
查看>>
jsp c标签不遍历的方法
查看>>
Linux命令:pigz多线程压缩工具【转】
查看>>
Python学习笔记——与爬虫相关的网络知识
查看>>
paip.php eclipse output echo 乱码
查看>>