您的位置:首页 > 其它

完美洗牌问题(打乱数组间各元素的顺序)

2016-05-04 16:32 381 查看
除了前一篇的完美洗牌问题,还有一种洗牌,就是乱序(shuffle)的问题,将54张有序的扑克牌的顺序打乱,实现洗牌操作。

局部洗牌法

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: