洗牌算法

2018-07-20    来源:open-open

容器云强势上线!快速搭建集群,上万Linux镜像随意使用

洗牌算法

给定一个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!
(●’?’●)

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

标签: swap 代码

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点!
本站所提供的图片等素材,版权归原作者所有,如需使用,请与原作者联系。

上一篇:C# winform 程序中响应键盘事件

下一篇:c#将图片所有像素转为深色调