背景:
洗牌对于棋牌游戏是必不可少的一个过程;
再有一类问题可以用洗牌算范完美解决:随机生成不重复的N以内的数
shuffle(洗牌)
查了有限的几个博客,洗牌算法按照出现的时间顺序总共有三种
1,Fisher–Yates Shuffle
该洗牌算法的主要思想是:
1,从原始序列的k个数里面随机取一个数m(randome(0,k)),
2,把m放入到输出序列中,
3,把原始序列从m到k依次往前移动,使原始序列剩余k-1个数;
4,重复1-3步骤,直到剩余0个数。
时间复杂度O(n2) 空间复杂度O(n)
2.Knuth-Durstenfeld Shuffle
该洗牌算法主要思想是:
首先继承了FY洗牌算法的思想,再在此算法基础上做了改进。具体表现为:
1,从原始序列的k个数里面随机取一个数m(randome(0,k)),
2,交换原始序列第m个数和第k个数的值,swap(array[m],array[k]),认为第k个数是已经处理过的数字
3,不再对原始序列第k个数进行任何操作,原始序列变为k-1个数,
4,重复1-3步骤,原始序列变为k-1个数。知道k=1为止。
时间复杂度O(n),空间复杂度O(n)
3,Inside-Out Algorithm
KD洗牌算法的优点是:时间复杂度和空间复杂度都比较好,KD洗牌算法是in-place的排序方法。
如果有这样一个特殊需求(什么应用场景,我也没用想到,只是隐隐感觉应该有这个需求):需要查看原始的未排序的数据。
那么KD洗牌算法就满足不了了,这个时候就需要应用Inside-Out Algorithm。
该算法的主要思想是:
1,首先拷贝一份原始序列。
2,从头到尾遍历拷贝序列,假设当前游标i,则生成[0,i]之间的随机数j,把原始序列的array[j]赋值给array[i];把拷贝序列的copy_array[i]赋值给原始序列的array[j],其实就是swap(array[i],array[j])。只是利用了拷贝序列,少了一次交换操作而已。
总结下:
跟KD算法的区别在于:
1,保留了原始序列,代价是多了一次原始序列的拷贝,以做他用。
2,KD算法取随机数的范围是从K开始越来越小;IO算法取随机数的范围是从1开始到K,越来越大。
3,KD算法是交换当前序列最大下标的值和生成的随机数对应的序列值;IO算法也是交换当前遍历序列的最大值和生成的随机数对应的序列值,后者利用了拷贝序列,所以交换次数少了1.