一个简单有效的洗牌算法

装配脑袋兄在某个帖子中指出了一种有意思的洗牌算法,博主按照他的思路写了另外一种洗牌算法。下面是该洗牌算法的思路:

我们先看一下纸牌游戏。一幅纸牌由 52 张不同的纸牌组成,发牌时必须产生不重复的纸牌,而且洗牌过程必须公平,即 52! 中纸牌顺序应该等概率出现。很明显这种随机排列所产生的随机数必须均匀分布且独立。由此代码如下:

using System;
using System.Diagnostics;

namespace Lucifer.CSharp.Sample
{
class Program
{
static void Main(string[] args)
{
//初始化牌局
int[] array = new int[52];
for (int i = 0; i < array.Length; i++)
{
array[i] = i;
}

//洗牌
Permute<int>(array);

//验证牌局
for (int i = 0; i < array.Length; i++)
{
var value = array[i];
for (int j = 0; j < array.Length; j++)
{
if (j == i) continue;
Debug.Assert(array[j] != value);
}
}
}

static void Permute<T>(T[] array)
{
Random random = new Random();
for (int i = 1; i < array.Length; i++)
{
Swap<T>(array, i, random.Next(0, i));
}
}

static void Swap<T>(T[] array, int indexA, int indexB)
{
T temp = array[indexA];
array[indexA] = array[indexB];
array[indexB] = temp;
}
}
}

代码示例中的 Permute<T>(T[] array) 方法产生一个随机序列。第一个循环用 1, 2, 3, …, N 初始化该序列。第二个循环完成一次随机洗牌。在该循环的每次迭代中,我们讲 array[j] 的值于数组位置在区间[0, j)之间的某个元素相交换(也可能不交换)。

然而,我们要问的是 Permute<T>(T[] array) 方法产生的所有排列等概率吗?

若根据该算法,答案为是。因为一共有 N! 种可能的排列,而 Swap<T>(array, i, random.Next(0, i)); 这一句 N-1 次调用 Next 方法出现的不同结果也是 N! 种。但是,事实上答案为否,并非所有排列都是等概率。问题就出在可爱的伪随机数数生成器(Pseudo-Random Number Generator)上。PRNG 的随机性很大程度上限制了随机序列的随机性。所以,上述代码需要一个更好的伪随机数数生成器以使实际与理论更加相符。但是好的 PRNG 往往伴随着性能的下降,比如 Mt19937 随机化算法。

题外话:博主在实际代码运行中,发现上述代码能很好的完成洗牌算法要求,但不保证其能在商业化项目中得以正确实践。我记得云风兄在他的博客上也曾经讨论过洗牌算法,有兴趣的兄弟可以搜索一番。

时间: 2024-10-07 09:46:04

一个简单有效的洗牌算法的相关文章

php实现简单洗牌算法_php技巧

如下所示: 复制代码 代码如下: <?php  /**   * 简单洗牌算法   */  $card_num=54; //牌数  print_r(wash_card($card_num));  function wash_card($card_num)  {      $cards=$tmp=array();      for($i=0;$i<$card_num;$i++){          $tmp[$i]=$i;      }      for($i=0;$i<$card_num;

[经典面试题]完美洗牌算法

题目 有个长度为2n的数组{a1,a2,a3,-,an,b1,b2,b3,-,bn},希望排序后{a1,b1,a2,b2,-.,an,bn},请考虑有无时间复杂度o(n),空间复杂度0(1)的解法. 来源 2013年UC的校招笔试题 思路一 第①步.确定b1的位置,即让b1跟它前面的a2,a3,a4交换: a1,b1,a2,a3,a4,b2,b3,b4 第②步.接着确定b2的位置,即让b2跟它前面的a3,a4交换: a1,b1,a2,b2,a3,a4,b3,b4 第③步.b3跟它前面的a4交换位

javascript随机之洗牌算法深入分析_javascript技巧

洗牌算法是我们常见的随机问题,在玩游戏.随机排序时经常会碰到.它可以抽象成这样:得到一个M以内的所有自然数的随机顺序数组. 在百度搜"洗牌算法",第一个结果是<百度文库-洗牌算法>,扫了一下里面的内容,很多内容都容易误导别人走上歧途,包括最后用链表代替数组,也只是一个有限的优化(链表也引入了读取效率的损失). 该文里的第一种方法,可以简单描述成:随机抽牌,放在另一组:再次抽取,抽到空牌则重复抽."抽到空牌则重复抽"这会导致后面抽到空牌的机会越来越大,显然

洗牌算法

几乎所有的程序员都写过类似于"洗牌"的算法,也就是将一个数组随机打乱后输出,虽然很简单,但是深入研究起来,这个小小的算法也是大有讲究.我在面试程序员的时候,就会经常让他们当场写一个洗牌的函数,从中可以观察到他们对于这个问题的理解和写程序的基本功.     在深入讨论之前,必须先定义出一个基本概念:究竟洗牌算法的本质是什么?也就是说,什么样的洗牌结果是"正确"的?     云风曾经有一篇博文,专门讨论了这个问题,他也给出了一个比较确切的定义,在经过洗牌函数后,如果能够

一步一步写算法(之洗牌算法)

原文:一步一步写算法(之洗牌算法) [ 声明:版权所有,欢迎转载,请勿用于商业用途.  联系信箱:feixiaoxing @163.com]     扑克牌洗牌是我们生活中比较喜欢玩的一个游戏.那么我们有没有什么办法自己设计一个扑克牌洗牌的方法呢?在c运行库当中有一个随机函数rand,它可以生成0~32767之间的任意数.那么有没有可能利用这么一个函数对我们扑克牌进行随即洗牌呢?     在这里我抛砖引玉一下,谈一谈自己目前已经看到的两个算法.欢迎朋友们谈一谈其他的方法.     (1)全局洗牌

JavaScript随机打乱数组顺序之随机洗牌算法_javascript技巧

假如有一个数组是这样子: var arr1 = ["a", "b", "c", "d"]; 如何随机打乱数组顺序,也即洗牌. 有一个比较广为传播的简单随机算法: function RandomSort (a,b){ return (0.5 - Math.random()); } 实际证明上面这个并不完全随机. 随便一搜网上太多这种东西了,看一下stackoverflow上的一个高分回答,答案出自github上. knuth-s

JS随机洗牌算法之数组随机排序_javascript技巧

推荐阅读:JavaScript学习笔记之数组的增.删.改.查 JavaScript学习笔记之数组求和方法 JavaScript学习笔记之数组随机排序 洗牌算法是一个比较形象的术语,本质上让一个数组内的元素随机排列.举例来说,我们有一个如下图所示的数组,数组长度为 9,数组内元素的值顺次分别是 1~9: 从上面这个数组入手,我们要做的就是打乱数组内元素的顺序: 代码实现 维基百科上的 Fisher–Yates shuffle 词条对洗牌算法做了详细介绍,下面演示的算法也是基于其中的理论编写的: A

[cocos2dx]斗地主制作之洗牌算法

做斗地主项目,洗牌算法是一个很重的一步,怎样"洗"的均匀,"洗"的随机,这是非常考究的,算法的优劣就直接会影响效果的好坏.这里我给出一个算法,将0-53这54个数字直接排序,经测试还挺随机的.这里要感谢@灰太龙的指导!这个算法是服务器端用于返回给客户端牌的算法,主要的思想就是不断的换牌,两牌交换位置,如果循环次数增大的话,随机性也会更强,洗牌的效果更好! Code: C#: using System; using System.Collections.Generic

JavaScript数组元素的排序及洗牌算法

这里利用了一个 sort 函数进行排序 正向排序 var numberArray = [2,1,3]; numberArray.sort(function(a, b){         return a-b;     } ); //[1,2,3] 逆向排序 var numberArray = [2,1,3]; numberArray.sort(function(a, b){         return b-a;     } ); //[3,2,1] 随机排序(洗牌) var numberArr