小算法:从N个对象中随机选择M个

小算法:从N个对象中随机选择M个

 

近日工作中遇到一个问题:在游戏中要求从50道题中随机选9道题出来给玩家。随即进行了一些思考,并将次过程整理成文记录于此。

 

从N个对象中随机选择M个作为输出是一个很常见的问题,现有的随机数生成器一般都只产生1个随机整数,因此解决这个问题的时候我们一开始可能会考虑这样解决问题:

时间复杂度O(M),空间复杂性O(1)。
当不要求选出来的M个对象必须是不同的对象时,这样做是没有问题的,即使M>=N,也就是说当要求输出的对象个数,比候选对象都要多的时候,都能正确输出。


首先假定候选对象集合中的对象互不相同,如果要求选出来的对象必须不同时(M<=N,也是必须保证的)。同时,为了保证不重复,需要随机选择之后,判断该对象是否已经选过了,如果选过了,就必须重选:

时间复杂度O(M*N),空间复杂性O(1)。
这样看起来没什么问题,但当M非常接近N时,选到后期,重选的概率非常大,而且每次都要进行将近M次比较才能。如果随即数产生函数不是太好的话,就像上面代码中用的C语言函数库中的随即数生成函数,从18个数中“随机”选择17个输出这样的工作似乎让程序陷入了死循环,因为到了后期,每次随机出来的数都已经和前面已选择的数据重复了。


鉴于上面的问题,看来必须使用点技巧来解决了。在此,我想到的办法是:每次随机选择一个后,我就将这个已经选择过的对象放到一个“角落”里,并在下一次选择时拒绝跑到那个角落里去选,这样一来,就能保证每次选到的数据都不会重复了:

时间复杂度O(M),空间复杂性O(N)。
创建一个新的数组pTemp,而不直接使用pSrcDatas,是为了不破坏pSrcDatas里的内容;pTemp只保存下标也是为了更好的节省空间,因为类型T可能要占用很大空间。
这种方法的确比上面那种方法好多了,毕竟,不管输入的数据是什么,它都能在固定的时间内完成任务,而且时间复杂度控制得很好。可惜pTemp空间的创建给程序的运行带来了一些不确定性,如果输入的候选对象数量庞大,这里的内存消耗也是很可观的;pTemp内存申请失败的话,程序无法继续运行。
不使用pTemp,就不能让选过的元素放到“选不到的地方”了。不真正进行移动操作,只是进行假想的移动,然后推算出该位置上的元素就可以了:

时间复杂度O(M*M),空间复杂性O(1)。
通常情况下,即使没有发生重复,上述算法也要进行(M-1)*M/2次检测,看是否发生了重复选择,平均每次选择随机数后要检测(M-1)/2次;如果发现选了重复的对象,算法便开始计算该位置上进行完假想的“移动”操作后的真实对象,极端情况下最多(M-1)*(M-1)次检索后找到该位置上进行完假想的“移动”操作后的真实对象。因此,这也引证了一句话:小概率事件发生时,只能通过更多的计算来弥补了。

最后,附上我的测试用例:

 

也欢迎就此问题进行深入探讨!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值