`

随机洗牌:哪一种算法是正确的?

 
阅读更多

原文

记得当年搞NOIp时,我犯过一个相当严重的错误:错误地把Floyd算法的i, j, k三层循环的位置顺序搞颠倒了。直到准备省选时我才突然意识到,Floyd算法应该最先枚举用于松驰操作的那个“中间变量”k,表示只经过从1到k的顶点的最短路;而我却一直习惯性地以为i, j, k应该顺次枚举。令人惊讶的是,这个错误跟了我那么久我居然从来都没有注意到过。后来,我发现有我这种经历的人不止一个。惯性思维很可能会让你接受一些明显错误的算法,并且让你用得坦坦荡荡,一辈子也发觉不了。
    假使你需要把一个数组随机打乱顺序进行重排。你需要保证重排后的结果是概率均等、完全随机的。下面两种算法哪一种是正确的?其中,random(a,b)函数用于返回一个从a到b(包括a和b)的随机整数。

1. for i:=1 to n do swap(a[i], a[random(1,n)]);
2. for i:=1 to n do swap(a[i], a[random(i,n)]);


    如果不仔细思考的话,绝大多数人会认为第一个算法才是真正随机的,因为它的操作“更对称”,保证了概率均等。但静下心来仔细思考,你会发现第二种算法才是真正满足随机性的。为了证明这一点,只需要注意到算法的本质是“随机确定a[1]的值,然后递归地对后n-1位进行操作”,用数学归纳法即可轻易说明算法的正确性。而事实上,这段程序一共将会产生n*(n-1)*(n-2)*...*1种等可能的情况,它们正好与1至n的n!种排列一一对应。
     有人会问,那第一种算法为什么就错了呢?看它的样子多么对称美观啊……且慢,我还没说第一种算法是错的哦!虽然第一种算法将产生比第二种算法更多的可能性,会导致一些重复的数列,但完全有可能每种数列重复了相同的次数,概率仍然是均等的。事实上,更有可能发生的是,这两种算法都是正确的,不过相比之下呢第一种算法显得更加对称美观一些。为此,我们需要说明,第一种算法产生的所有情况均等地分成了n!个等价的结果。显然,这个算法将会产生n^n种情况,而我们的排列一共有n!个,因此n^n必须能够被n!整除才行(否则就不能均等地分布了)。但是,n!里含有所有不超过n的质数,而n^n里却只有n的那几个质因子。这表明要想n^n能被n!整除,n的质因子中必须含有所有不超过n的质数。这个结论看上去相当荒唐,反例遍地都是,并且直觉上告诉我们对于所有大于2的n这都是不成立的。为了证明这一点,只需要注意到2是质数,并且根据Bertrand-Chebyshev定理,在n/2和n之间一定还有一个质数。这两个质数的乘积已经大于n了。搞了半天,第一种看似对称而美观的算法居然是错的!

分享到:
评论

相关推荐

    洗牌发牌模拟系统课程设计报告--C语言

    附录B的洗牌和发牌算法有意使用了一种低效的洗牌算法,它有可能会导致无限延 迟。建立一种高效的洗牌算法,这种算法能够避免无限延迟。 对洗牌算法作如下修改。先照图7-28初始化数组deck,再修改函数shuffle使它...

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

    洗牌算法是我们常见的随机问题,在玩游戏、随机排序时经常会碰到。它可以抽象成这样:得到一个M以内的所有自然数的随机顺序数组。 在百度搜“洗牌算法”,第一个结果是《百度文库-洗牌算法》,扫了一下里面的内容,...

    拱猪游戏源码2012929

    下面介绍一下该算法的一种实现方式。 洗牌和发牌模拟 首先给扑克牌中每张牌设定一个编号,下面算法实现的编号规则如下: 红桃按照从小到大依次为:1-13 方块按照从小到大依次为:14-26 黑桃按照从小到大依次为:27...

    拱猪游戏代码

    下面介绍一下该算法的一种实现方式。 洗牌和发牌模拟 首先给扑克牌中每张牌设定一个编号,下面算法实现的编号规则如下: 红桃按照从小到大依次为:1-13 方块按照从小到大依次为:14-26 黑桃按照从小到大依次为:27-...

    计算机算法设计与分析(共30张PPT).pptx

    此时可借助于随机预处理技术,不改变原有的确定性算法,仅对其输入进行随机洗牌,同样可收到舍伍德算法的效果。例如,对于确定性选择算法,可以用下面的洗牌算法shuffle将数组a中元素随机排列,然后用确定性选择算法...

    C#棋牌游戏,扑克洗牌、发牌算法源代码

    下面介绍一下该算法的一种实现方式。    洗牌和发牌模拟。    首先给扑克牌中每张牌设定一个编号,下面算法实现的编号规则如下:  红桃按照从小到大依次为:1-13  方块按照从小到大依次为:14-26  黑桃按照...

    C语言实现随机发扑克牌

    打散即洗牌:通过rand以及srand函数来获得,为了避免相同的序列,使用标准时间来作为序列种子。 void shuffle(int *cards, int lenth) { int temp, i, index; time_t t; srand((unsigned int)(&

    mld_ga_basic(popSiz e, numIter, len, wid, load ):Tournament 遗传算法,用于计算作业车间布局的最小成本设计(负载 x 距离)-matlab开发

    (3) Shuffle (重新洗牌) 流行新锦标赛(4) 子组弹出为 4 组。 一种。 找到4个最好的湾覆盖子组流行音乐中最差的 4 个(5) 变异每个子组中最好的 4 个(获胜者) (6) 将最好的 4(优胜者)和所有突变插入到种群中(7) ...

    Java中打乱一个数组的2种公平算法分享

    主要介绍了Java中打乱一个数组的2种公平算法分享,本文讲解了洗牌程序原理、生成随机索引交换二种方法并给出示例代码,需要的朋友可以参考下

    ShuffleFastaSeq:该应用程序可用于以FASTA格式对序列进行洗牌。-开源

    ShuffleFastaSeq是一种Windows窗体应用程序,用C#编写,可以将FASTA格式的序列进行混洗。 使用Durstenfeld(https://en.wikipedia.org/wiki/Fisher–Yates_shuffle)实现的Fisher-Yates算法的简单变体即可获得随机...

    用改进的蛙跳算法求解一类模糊Flow Shop调度问题 (2010年)

    对加工时间不确定的Flow Shop调度问题进行研究,提出了一种改进的蛙跳算法(New Shuffled Frog Leaping Algorithm,NSFLA)。蛙跳算法(Shuffled Frog Leaping Algorithm,SFLA)的局部搜索采用类似粒子群算法的搜索机制,...

    像计算机科学家一样思考Python(第2版).pdf

    18.6 添加、删除、洗牌和排序 185 18.7 继承 186 18.8 类图 188 18.9 数据封装 189 18.10 调试 190 18.11 术语表 191 18.12 练习 191 第19章 Python拾珍 194 19.1 条件表达式 194 19.2 列表理解...

    数据挖掘报告.docx

    又称数据库中的知识(Knowledge Discovery in Database, KDD),是从大量的、不完全的、有噪声的、模糊的和随机的数据中,提取隐含在其中的、人们事先不知道的,但又是潜在有用的信息和知识的过程。数据挖掘是一门...

Global site tag (gtag.js) - Google Analytics