每日算法洗牌算法

Posted jiange_zh

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日算法洗牌算法相关的知识,希望对你有一定的参考价值。

洗牌算法

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


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

以上是关于每日算法洗牌算法的主要内容,如果未能解决你的问题,请参考以下文章

洗牌算法

洗牌算法

VB洗牌算法产生随机数组

常用算法篇-洗牌算法

扑克牌的完美洗牌算法

随机洗牌算法Knuth Shuffle和错排公式