【每日算法】洗牌算法

洗牌算法

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


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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值