Javascript随机洗牌算法的深入分析
洗牌算法是一种常见的随机问题,它在游戏中经常遇到,随机排序。在百度搜索洗牌算法中,第一个是百度文库扫过的洗牌算法,里面的内容,很多内容容易误导人误入歧途,包括最后的列表代替数组,只有有限的优化(列表还引入了读取效率的损失)。
本文中的第一种方法可以简单地描述为:随机抽取卡片,放置在另一组中。
一次又一次地抽空牌是不合理的,这会导致越来越多的机会在后面画空牌。
它可以优化一步:在卡被取走后,原卡较少。(而不是留下一张空卡)
代码如下:
复制代码代码如下所示:
功能shuffle_pick_1(M) / / / /洗牌绘制方法
{
生成卡
var arr =新的数组(M);
对于(var i = 0;i < m;i + +){
ARR {我} =我;
}
每次在另一个堆中的一张卡片,是时候取出数组中的元素并将所有元素向前拉。
VaR arr2 =新的数组();
对于(var i = m;i 0;i){
VaR RND = math.floor(Math.random)*(我);
arr2.push(ARR {和});
Arr.splice(RND,1);
}
返回arr2;
}
这显然也是有问题的,因为如果数组很大,在中间删除一个元素将导致向前一步。这是一个耗时的行动。
为什么要删除该元素目的是不生产空卡。
除了删除该元素之外,我们还有其他方法来删除空卡吗
是的,我们把最后一张不排水卡放在那个位置了。
因此,我们可以优化这种思维方式。
复制代码代码如下所示:
功能shuffle_pick(M) / / / /抽奖卡洗牌法
{
生成卡
var arr =新的数组(M);
对于(var i = 0;i < m;i + +){
ARR {我} =我;
}
/ /每次在另一桩卡。把最后动用卡上的空间。
VaR arr2 =新的数组();
对于(var i = m;i = 0;){
VaR RND = math.floor(Math.random)*(我);
arr2.push(ARR {和});
{和} = { ARR ARR我};
}
返回arr2;
}
除了思路外,我们还可以用换牌的想法。
百度库-洗牌算法是指一种新的换牌思路:随机交换两个位置,总交换N次,越大N,越接近随机。
这个错了。即使n是非常大的(例如,10张卡片,10个变化),很可能有些卡片根本没有改变。
随着这个想法,稍微调整一下就可以了:我张和任何一张卡换座位,换一个圆。
代码如下:
复制代码代码如下所示:
功能shuffle_swap(M) / / / /洗牌法
{
生成卡
var arr =新的数组(M);
对于(var i = 0;i < m;i + +){
ARR {我} =我;
}
我和任何一张牌都动了,换一个轮子就可以了。
对于(var i = 0;i < m;i + +){
VaR RND = math.floor(Math.random()*(i + 1)),
温度= {和} ARR;
ARR { } = {我} RND ARR;
ARR {我} =温度;
}
报酬;
}
除了画和变的思路外,我们还可以用插牌思维:一张牌,二张牌有两个位置可以随机插入(第一张牌前后),第三张牌有三个位置(随机插在背面,或插在第一位,或插在第二),等等。
代码如下:
复制代码代码如下所示:
功能shuffle_insert_1(M) / / / /插入洗牌法
{
每次都会随机地在卡片中插入一个最大的卡片,因为元素被插入到数组中,后面的所有元素都被压缩回一位,所以这是耗费时间的。
var arr = { 0 };
对于(var i = 1;i < m;i + +){
Arr.splice(math.floor(Math.random()*(i + 1)),0,我);
}
报酬;
}
上面的代码也会有一些问题:随着卡片数量的增加,插入卡变得越来越困难,因为卡会导致许多卡片向后移动。
当然,我们也可以适当地优化它:第一n-1卡,最后的n卡,然后任何交换位置。
代码如下:
复制代码代码如下所示:
功能shuffle_insert(M) / / / /洗牌插牌的优化版本,可以用数学归纳法证明,搅和均匀。
{
每次最多一张卡,随机换张牌。
var arr =新的数组(M);
ARR { 0 } = 0;
对于(var i = 1;i < m;i + +){
VaR RND = math.floor(Math.random()*(i + 1));
ARR {我} = {和} ARR;
{和} =我到达;
}
报酬;
}
好的,所有的代码如下所示。感兴趣的学生可以在自己的机器上试试看,它们的执行效率和最终结果是否是理论随机的。
复制代码代码如下所示:
javascript洗牌算法
功能shuffle_pick_1(M) / / / /洗牌绘制方法
{
生成卡
var arr =新的数组(M);
对于(var i = 0;i < m;i + +){
ARR {我} =我;
}
每次在另一个堆中的一张卡片,是时候取出数组中的元素并将所有元素向前拉。
VaR arr2 =新的数组();
对于(var i = m;i 0;i){
VaR RND = math.floor(Math.random)*(我);
arr2.push(ARR {和});
Arr.splice(RND,1);
}
返回arr2;
}
功能shuffle_pick(M) / / / /抽奖卡洗牌法
{
生成卡
var arr =新的数组(M);
对于(var i = 0;i < m;i + +){
ARR {我} =我;
}
/ /每次在另一桩卡。把最后动用卡上的空间。
VaR arr2 =新的数组();
对于(var i = m;i = 0;){
VaR RND = math.floor(Math.random)*(我);
arr2.push(ARR {和});
{和} = { ARR ARR我};
}
返回arr2;
}
功能shuffle_swap(M) / / / /洗牌法
{
生成卡
var arr =新的数组(M);
对于(var i = 0;i < m;i + +){
ARR {我} =我;
}
我和任何一张牌都动了,换一个轮子就可以了。
对于(var i = 0;i < m;i + +){
VaR RND = math.floor(Math.random()*(i + 1)),
温度= {和} ARR;
ARR { } = {我} RND ARR;
ARR {我} =温度;
}
报酬;
}
功能shuffle_insert_1(M) / / / /插入洗牌法
{
每次都会随机地在卡片中插入一个最大的卡片,因为元素被插入到数组中,后面的所有元素都被压缩回一位,所以这是耗费时间的。
var arr = { 0 };
对于(var i = 1;i < m;i + +){
Arr.splice(math.floor(Math.random()*(i + 1)),0,我);
}
报酬;
}
功能shuffle_insert(M) / / / /洗牌插牌的优化版本,可以用数学归纳法证明,搅和均匀。
{
每次最多一张卡,随机换张牌。
var arr =新的数组(M);
ARR { 0 } = 0;
对于(var i = 1;i < m;i + +){
VaR RND = math.floor(Math.random()*(i + 1));
ARR {我} = {和} ARR;
{和} =我到达;
}
报酬;
}
/ /警报(shuffle_pick(10))
无功funcs = { shuffle_pick_1,shuffle_pick,shuffle_swap,shuffle_insert_1,shuffle_insert },
funcnames = {抽水
m=10000,
时间= {;
对于(var i = 0;i < funcs.length;i++){
var D0 =新的日期();
funcs {我}(M);
我funcnames { } =(新)-(D0)+ T + funcnames {我};
}
警报(funcnames.join(' ;