| 注册
请输入搜索内容

热门搜索

Java Linux MySQL PHP JavaScript Hibernate jQuery Nginx
su1216
8年前发布

洗牌算法

洗牌算法

给定一个n个数的序列,设计一个算法将其随机打乱,保证每个数出现在任意一个位置的概率相同(也就是说在n!个的排列中,每一个排列出现的概率相同)。

朴素的做法:

假设输入为数组num[length]。

随机选一个数,放到num[0]中,再随机选数,如果该数已经选过,重新选,直到该数未选过时放入num[1]中,以此类推,直到所有的数都选出来,很明显,这种选法一共有n!中可能,每种可能出现的概率都相同。

但是该做法效率不高,因为选过的数再选将耗费大量时间。

改进的洗牌算法:

基于以上算法的缺陷,我们做出改进:选过的数将不再考虑。比如num[0…k]为已选的数,那么我们的随机只在k+1到length-1间进行。

void MySwap(int &a, int &b)  {      int tmp = x;      x = y;      y = tmp;  }    void Shuffle(int num[], int length)  {      if (NULL == num || length <= 0) return;      for (int index = length-1; index >= 1; --index)      {          MySwap(num[index], num[rand()%(index+1)]);      }  }

解释一下:

index为本次选的牌的存放位置,rand()%(index+1)产生0到index之间的随机数,而已选的数的下标为index+1到length-1,所以可以保证已选的数不会再重复选到。

一些小细节:

习惯使用–index而不是index–的原因是:index–需要一个临时变量来保存自减前的index值,而–index,直接先自减,之后返回自身,因此效率更高一些。(当然,实际上编译器可能会做一些优化,使得两者的区别不大,这仅仅是一个良好的编程习惯)。

使用逆序:rand()%(index+1)比较方便地产生0到index之间的随机数,如果是正序,则需要写成:

    for (int index = 0; index < length; ++index)      {          MySwap(num[index], num[index+rand()%(length-index)]);      }

运算多一些,而且不够简洁。

最后提一个点:以上代码每次运算的结果都会是一样的,如果想要每次都不一样,需要添加种子(使用srand((int)time(0));根据时间来选择种子)。

每天进步一点点,Come on!
(●’◡’●)

本人水平有限,如文章内容有错漏之处,敬请各位读者指出,谢谢!