洗牌算法

说来比较惭愧,一个暑假没学什么东东,平时都瞎忙活了.唯一值得总结的可能也就是这个洗牌算法.

这个算法的要求是这样的:将N个数乱序后输出.由于和扑克牌的洗牌过程比较相似所以我也就称为洗牌算法了.很多地方都不自觉的需要这个算法的支持.也可以将这个算法扩展为从N个数中取出M个不重复的数(0<M<=N).

在大四的时候曾经在做某XX系统的时候遇到过这个问题,当时是要从N道题中选出M道然后出题.当时想到的是最简单也是最低效的一种方法:从N个数中随机取出一个放入一个新的集合中,看集合中是否已经存在这个数,如果没有就继续,否则重新生成随机数.由于当时需要的题目量比较少,因此也就蒙混过关了.后来一想如果每次生成的随机数都一样岂不是很慢?当时采用的还是”高科技”:HashSet.Java的HashSet有这个一个好处,就是添加的数如果已经存在就不进行操作,省得再判断是否存在了.唉,用了java就是容易偷懒啊!

算法(Java):

public HashSet getRamdomNumber(int total,int number){

//在0到total-1中随机生成number个不重复的随机数

HashSet h=new HashSet();

while(h.size()<number){

Random r=new Random();

int n=r.nextInt(total);

//n++;

h.add((Integer)n); //不用考虑重复的情况

}

return h;

}

第二种方法是从网上看到的,思路是:将数组的元素进行随机的交换.这个算法我不太好说,因为它的效果和效率都依赖于交换的次数N.次数越多则乱序的程度越高但是相应的效率也会降低.个人不太推荐这个算法,因为N的次数不好确定.

算法:
void shuffle ( int a[], int n ) //洗牌算法

{

int tmp = 0,p1,p2;

int cnt = rand() % 1023;

while (cnt--) //随机交换两个位置的数,共交换cnt次

{

p1 = rand() % n;

p2 = rand() % n;



tmp = a[p1];

a[p1] = a[p2];

a[p2] = tmp;

}

}
后来在看Core Java 2的时候看到了一个计算彩票的问题,实际上就是洗牌算法.思路是这样的:从数组中随机取出一个放到一个新的集合中,然后重复这个工作.这样最后原数组为空而新的集合则为乱序.这个算法比第一种算法的效率高多了,而且计算次数是固定的.当时就以为找到了最佳算法.后来蒙朋友提醒,对这个算法进行了改进.其实没有必要开辟一个新的集合的,用原来的存储空间就行.具体思路是:从数组的数中随机取出一个和最后一个元素交换,再从前面N-1个数中随机取一个和倒数第二个交换…这样可以达到和前面算法相同的效果而且存储空间也得到了节省,很不错的算法了.

算法:

void shuffle()

{

int *prev=new int[54];

for(int i=0;i<54;i++) //数组的初始化,表示54张牌

{

prev[i]=i;

}

srand(time(0));

int times=53;

while(times!=0)

{

int one=rand()%times;

swap(prev[one],prev[times]); //交换元素

times--;

} 

//这个时候prev数组处于乱序状态,用完后回收空间

delete prev;

}



本来到这里就接近尾声了,但是后来看到stl中还提供了random_shuffle()方法.这个方法的效果就是实现洗牌算法.具体的效率方面我还没有测试过,不知道我的算法和它的哪个更快.以后有机会试一下.也许它用的就是我的思路也说不定,哈哈.
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值