完美洗牌问题(打乱数组间各元素的顺序)
2016-05-04 16:32
381 查看
除了前一篇的完美洗牌问题,还有一种洗牌,就是乱序(shuffle)的问题,将54张有序的扑克牌的顺序打乱,实现洗牌操作。
2。随机产生一个1-(n-1)的数y,如果
3。随机产生一个1-(n-i)的数z,取第z个没有被抽出来的作为第i张牌。(i=3,4,5…54)
这种算法的复杂度为O(N^2),因为计算每个随机数的牌号平均要执行(N/2)次比较。
对应于现实中的扑克牌,这种算法等于每次从牌堆中随机抽一张,放到另一堆上,直到抽完为止,这里新的一堆就是洗完的牌序。
第一种优化就是每次抽出一张牌后,就将之后的所有牌向前移动一个单位,下次产生的随机数就是目标牌的下标。
如第一次随机数为3,抽出3,扑克牌数组变为1 2 4 5 6 7…
第二次抽出6,直接取数组的第六个元素即7,然后把7之后的牌前移一个单位。这样牌的定位复杂度就是1了,但是移动的复杂度仍为N/2,所以这种算法并没有起到效率优化的目标。
第二种优化是为树状数组。用一个长为54的树状数组 used 存储 1-i 张牌中被投抽出来的牌的张数。这样 i-used[i] 就是牌号为 i 的扑克的序号。然后用2分查找的方式,就可以快速确定随机数对应的牌号,树状树的复杂度为logn,这样总的算法复杂度为O(NlogN)。
但是考虑到一种54张牌,树状数组优化带来的性能提升很小,比起程序代码带来的可维护性损失,颇不值得。
2。随机产生一个1-n的数y,然后让第y张牌和第2张牌互相调换。
3。随机产生一个1-n的数z,然后让第z张牌和第i张牌互相调换。(i=3,4,5…54)
这种算法的复杂度为O(N)。
代码如下
参考网址
【1】洗牌算法 - hankjin的日志 - 网易博客
http://hankjin.blog.163.com/blog/static/3373193720109141128016/
【2】扑克牌洗牌算法 - CC专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/oktears/article/details/19435613?utm_source=tuicool&utm_medium=referral
【3】扑克牌的完美洗牌算法 - borey的个人空间 - 开源中国社区
http://my.oschina.net/xlplbo/blog/312231?fromerr=DLYCrycF
局部洗牌法
1。随机产生一个1-n的数x,做为第一张牌。2。随机产生一个1-(n-1)的数y,如果
y<x,则将y作为第二张牌,否则将y+1作为第二张牌。
3。随机产生一个1-(n-i)的数z,取第z个没有被抽出来的作为第i张牌。(i=3,4,5…54)
这种算法的复杂度为O(N^2),因为计算每个随机数的牌号平均要执行(N/2)次比较。
对应于现实中的扑克牌,这种算法等于每次从牌堆中随机抽一张,放到另一堆上,直到抽完为止,这里新的一堆就是洗完的牌序。
改进:
方法一由于计算随机数对应的牌号平均要执行(N/2)次比较,所以复杂度为O(N^2),N*N的第一个N是牌的数目,无法优化,但是第二个N是确定牌的序号,这个是否有办法优化呢?第一种优化就是每次抽出一张牌后,就将之后的所有牌向前移动一个单位,下次产生的随机数就是目标牌的下标。
如第一次随机数为3,抽出3,扑克牌数组变为1 2 4 5 6 7…
第二次抽出6,直接取数组的第六个元素即7,然后把7之后的牌前移一个单位。这样牌的定位复杂度就是1了,但是移动的复杂度仍为N/2,所以这种算法并没有起到效率优化的目标。
第二种优化是为树状数组。用一个长为54的树状数组 used 存储 1-i 张牌中被投抽出来的牌的张数。这样 i-used[i] 就是牌号为 i 的扑克的序号。然后用2分查找的方式,就可以快速确定随机数对应的牌号,树状树的复杂度为logn,这样总的算法复杂度为O(NlogN)。
但是考虑到一种54张牌,树状数组优化带来的性能提升很小,比起程序代码带来的可维护性损失,颇不值得。
全局洗牌法
1。随机产生一个1-n的数x,然后让第x张牌和第1张牌互相调换。2。随机产生一个1-n的数y,然后让第y张牌和第2张牌互相调换。
3。随机产生一个1-n的数z,然后让第z张牌和第i张牌互相调换。(i=3,4,5…54)
这种算法的复杂度为O(N)。
代码如下
void get_rand_number(int array[], int length) { int index; int value; int median; if(NULL == array || 0 == length) return ; /* 每次发牌的时候任意分配待交换的数据 */ for(index = 0; index < length; index ++){ value = rand() % length; median = array[index]; array[index] = array[value]; array[value] = median; } }
递归思想
我们有n张牌,不妨先假设有一个洗牌函数shuffle(….),能完美的洗出n-1张牌 。拿第n张牌来打乱前面n-1的洗牌顺序,从而得到n张牌的最终结果。#include <iostream> #include <cstdlib> using namespace std; //随机指定区域内的数 int MyRand(int low, int high) { return low + rand() % (high - low + 1); } int* shuffle(int* cards, int n) { if (n <= 0) return cards; shuffle(cards, n - 1); int rand = MyRand(0, n); int temp = cards[rand]; cards[rand] = cards ; cards = temp; return cards; } int main() { for (int k = 1; k <= 10; k++) { int cards[52] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, }; cout << endl; shuffle(cards, 52);// 洗牌 for (int i = 1; i <= 52; i++) { cout << cards[i - 1] << " "; if (i % 13 == 0) cout << endl; } } cout << endl; system("PAUSE"); return 0; }
参考网址
【1】洗牌算法 - hankjin的日志 - 网易博客
http://hankjin.blog.163.com/blog/static/3373193720109141128016/
【2】扑克牌洗牌算法 - CC专栏 - 博客频道 - CSDN.NET
http://blog.csdn.net/oktears/article/details/19435613?utm_source=tuicool&utm_medium=referral
【3】扑克牌的完美洗牌算法 - borey的个人空间 - 开源中国社区
http://my.oschina.net/xlplbo/blog/312231?fromerr=DLYCrycF
相关文章推荐
- 骆驼命名法
- 排序算法-选择排序 Selection Sort
- MySQL 5.7.12源码安装实例
- 算法系列之十四:狼、羊、菜和农夫过河问题
- solr全文检索(第二篇 solr的实例)--源自技术
- http的状态码 300-301-302-303-304-305-307
- jsp ,servlet中session ,Cookie 相关知识
- 拍照(从本地获取存储在SD卡中)
- 【翻译】Xibo官方文档12-Layout-Region
- c#生成方案里预生成拷贝文件
- 从配置读取一段时间(TimeSpan)
- python 多进程 logging:ConcurrentLogHandler
- JVM中的G1垃圾收集器
- python 多进程 logging:ConcurrentLogHandler
- 获取当前设备可用内存及所占内存的头文件
- Cocos2d-lua自动重新加载SpriteFrames
- 优雅的图片翻转实现方式rollover.js
- 采用泳道图工具跟踪项目进度或者问题解决进度
- 数据库查询(主键作为条件)
- C++类中this指针的理解