关于洗牌算法的一点总结

来源:互联网 发布:星河战队3 知乎 编辑:程序博客网 时间:2024/06/06 08:51
之前写斗地主的时候简单写了一个洗牌函数,基本思路是先产生一个顺序数组,遍历数组,每次产生一个(1~n)随机数,把这个随机数作为下标取出数组里的数与当前位置的数交换。
当时也没多想,反正能打乱数组顺序就行,后来跟师兄吃饭的时候聊起来,说到面试里也出现过洗牌算法,问我怎么保证这个算法得到的数组是完全随机的(即等概),我一时竟不知怎么证明。
回来后上网找了一下,才发现原来这个算法竟然不是完全随机的。

下面是一些思考和总结:(假设牌数为n)
  1. 首先最容易想到的一种等概的算法就是每次从数组里随机不放回地取出一个数,循环n次,刚好把数取完。
这个算法难点在于如何实现不放回。常规做法有两种:a.删掉。但如果是采用数组方式存储需要移动后面的所有数,而如果采用链表每次遍历也会影响效率,所以这种方式不可取。b. 标记。如果取到的数已标记,则重新产生随机数。这种方式在于越到后面,标记的位置越多,产生新的随机数越难,也不可取。
然后我就看到了这篇:随机问题之--洗牌算法
可以用交换位置的方式,每次取完之后就把取牌位置的牌换成最后一张牌,然后总牌数减1,下一次就在剩下的牌中取,因为每次取票的位置随机,所以即使交换了位置取到的牌还是随机的。所以总结一下就是每次在1到(n-i)中取一张牌,然后与(n-i)交换位置,i=0~(n-2).
证明其随机性:第一次有n种选择,放在n处,将n处隔离开;下一次有n-1个选择,放在n-1处...所以总共有n!种不重复的组合方式,而n张牌刚好也是有n!种组合方式,所以涵盖了所有可能组合方式,且不重复,足证等概。

2.局部整理法。假如有n张牌已经随机排列,此时新增一张牌,要如何使得新的牌随机排列?只需要将新增的牌随机放入一个位置即可,我们可以生成一个随机数,将随机数所在位置的数与最后一个位置(新牌)交换。利用这种思想,我们可以先整理局部的牌,然后慢慢地加牌进来。而这个“局部”,递归到最后就是一张牌,一张牌本身就是“随机”的排列,然后我们加入另一张牌,与其随机交换...最终的实现方式就是:
在1~i之间产生一个随机位置,与i位置的牌交换,i=2~n.
对比第一种方式,其实是一样的,只不过是反过来而已。

3.回头再看我之前写的洗牌函数,为什么我说它不是完全随机的呢?对比一下上两种方式唯一的不同就是我产生的随机数是(1~n),由于每次都有n种可能,循环n次之后便有了n*n种可能的组合方式,当然是有重复的,而当n>2时,n*n明显不是n!的倍数,所以每种组合的重复次数并不相同,所以不是完全随机的。
参考:随机洗牌算法 
0 0
原创粉丝点击