您现在的位置是:抱朴含真网 > 百科
【洗牌算法】你确定这样的抽奖算法是随机的?
抱朴含真网2024-05-06 06:26:04【百科】6人已围观
简介洗牌算法在实际应用中使用的比较广泛,比如抽奖、三国杀游戏等等。由于要完全理解洗牌算法存在一定的难度,所以洗牌算法也经常被拿来做算法笔试题。例如以下两个常见的笔试题:在n个不同的数中随机取出不重复的m个
洗牌算法在实际应用中使用的洗牌比较广泛,比如抽奖、算法算法随机三国杀游戏等等。确定由于要完全理解洗牌算法存在一定的抽奖难度,所以洗牌算法也经常被拿来做算法笔试题。洗牌例如以下两个常见的算法算法随机笔试题:在n个不同的数中随机取出不重复的m个数;打乱一副扑克牌,不能用额外空间,确定证明为什么是抽奖随机的?
在平时开发中,我们经常会用到随机数,洗牌而我们一般要生成随机数时,算法算法随机都是确定使用Random类实现。然而Random 类实现的抽奖随机数是真正的随机数吗?Java中的Random又是如何实现随机数的生成呢?
Random是通过一个种子通过一定的算法来获取的随机数,实现的洗牌算法有很多,在Java的算法算法随机Random类中的算法如下:
public int nextInt() { return next(32);}protected int next(int bits) { long oldseed, nextseed; AtomicLong seed = this.seed; do { oldseed = seed.get(); nextseed = (oldseed * multiplier + addend) & mask; } while (!seed.compareAndSet(oldseed, nextseed)); return (int)(nextseed >>> (48 - bits));}
如果种子一样,产生的确定随机数也是一样的,这种计算随机数不满足随机性,所以我们把Random生成的随机数通常称为伪随机数。通常随机数是无法预先计算出来的,并且每个数字出现的概率是一样的。随机数必须满足以下两个条件:
- 不可计算性,即在随机数产生前,不能通过任何方式计算出来。
- 机会均等性,即需要保证每个数出现的概率是相等的。
在平常生活中,通过掷骰子的方式就可以很容易获取一个随机数。但在计算机中,要生成一个随机数,却不是一件简单的事。首先,我们需要考虑在一定范围内如何随机
- Fisher-Yates Shuffle洗牌算法
假设我们有一个数组a[1,2,3],我们需要实现一个算法,将顺序的1,2,3随机的打乱在数组中。而这里的随机是表示每个数最终落在每个位置的概率都需要是一样的。我们可以通过随机抽取数组,之后将抽取的数放入到另一个数组的位置的方式来实现随机打乱数组,例如,
- 新数组的第一个位置抽取1的概率为1/3(1/n),而抽取不到1的概率为2/3(n-1/n);
- 当第一个位置抽取到了1,第二个位置抽取2的概率为1/2(在第二步的概率,不是整个过程的概率),那么在整个过程中第二个位置抽取到数字2的概率是第一个位置抽取不到数字2的概率*第二个位置抽到到数字2的概率,即2/3*1/2=1/3,即(n-1/n)*(1/n-1)=1/n;
- 依次类推,在前面两个抽取为数字1,2时,在新数组第三个位置被抽到3的概率为1,而第一步和第二步抽取不到3的概率分别为2/3、1/2,所以整个过程中第三个位置抽取到数字3的概率为第一个位置抽取不到数字3的概率*第二个位置抽取不到数字3的概率*第三个位置抽到到数字3的概率,即2/3*1/2*1=1/3,即(n-1/n)*(1/n-1)*1=1/n。
依次类推,一个元素m被放入第i个位置的概率P = 前i-1个位置选择元素时没有选中m的概率 * 第i个位置选中m的概率,即
在 i = 0 的情况下,很显然p=1/n。对于 i > 0 的情况,前一个式子的分子正好能把下一个式子的分母约去,到最后也只有第一个式子分母还在。因此,不管是哪一轮摸到了哪一个数,概率都是1/n ,所以这个数组的每个排列组合都是等概率的。
以上这种类似抽牌来实现的洗牌算法,这个算法是由两个科学家 Fisher 和 Yates提出的,所以人们称之为Fisher-Yates Shuffle算法。我们可以通过Java来实现一份Fisher-Yates Shuffle算法打乱一份扑克牌的代码:
private Random rand = new Random();private String[] b;public static void shuffle(String[] a){ int n = a.length; for (int i = 0; i < n; i++) { // Exchange a[i] with random element in a[i..N-1] int r = rand.nextInt(n-i); String temp = a[r]; b[i] = temp; a.remove(temp); }}
Fisher-Yates Shuffle算法的时间复杂度是O(n),空间复杂度是O(n)。
- Knuth-Durstenfeld 洗牌算法
Fisher-Yates Shuffle算法的特点是通过随机抽取的方式来实现的,这种洗牌方式的缺点就是需要额外的空间来存储随机抽取的数字,那有没有一种算法可以省去额外的存储空间呢?
Knuth 和 Durstenfeld 在Fisher-Yates Shuffle算法研究的基础上对洗牌算法进行了改进,通过将随机抽取的数字与在原来数组上的数字进行交换,这样就省去了额外的空间,降低了空间复杂度。
private Random rand = new Random();public static void shuffle(String[] a){ int n = a.length; for (int i = 0; i < n; i++) { // Exchange a[i] with random element in a[i..N-1] int r = rand.nextInt(n-i); String temp = a[i]; a[i] = a[r]; a[r] = temp; }}
这样洗牌算法的时间复杂度是O(n),而空间复杂度是O(1)。
很赞哦!(16)
上一篇: 蜀门手游哪个职业好最强职业推荐
下一篇: 韩酒驾男歌手反省八个月后复出开演唱会
站长推荐
友情链接
- 《LOL》索拉卡炫彩皮肤怎么样 索拉卡皮肤上线时间介绍
- 热血江湖手游等级提升攻略,热血江湖手游等级攻略
- 手游蜀门后期职业厉害吗
- LOL索卡拉至臻皮肤在哪领取 索拉卡星之守护者至臻怎么领取
- 《精灵盛典》属性提升——加点攻略详解(附肝帝视频)
- 摩尔庄园手游刺头鱼骨架获得方法
- 《星球大战:最后的绝地》设定解读与彩蛋梳理
- 时空猎人VIP21充值价格是多少?了解VIP21充值费用详情
- 原神怎么前往神秘小岛 原神神秘小岛任务怎么触发
- 大话西游3科举殿试(大话西游手游殿试科举答题器)
- 英魂之刃华为版官方版
- 【洛克王国---活动抢先知】2024年祈福节版本部分情报公开(新壁纸)
- 沙城征战
- 原始征途怎么快速升级 新手快速快速升级攻略教程
- 玩家攻略
- 洛克王国2022年费多少钱
- 烟雨江湖长生诀怎么样 长生诀属性分享
- 造梦西游3修改器无限点券 v1.0.0
- 烟雨江湖淬炼怎么省钱 淬炼省钱攻略
- 【新时代文明实践】倡导喜事新办 助推移风易俗
- 数据仓库+大姐姐们的高级别墅:惠普 MicroServer ProLiant Gen8 G1610T 微型服务器
- 龙途天下征途手游:选择合适流派提升角色战斗力
- 梦幻西游坐骑属性加力还是体,梦幻西游坐骑什么属性点灵力
- 4月1日关于部分玩家VIP充值无法获得相应兑换道具的情况说明
- 萌新答疑丨常见问题faq大全
- 神鬼战士HD 不能登入
- LOL无限火力巨魔出装推荐 无限火力巨魔玩法教学
- 原创神鬼传奇怎么双开、多开?神鬼传奇双开助手工具下载安装教程
- 七雄争霸为什么不火了 七雄争霸手游还有人玩吗
- 征途2家族累计充值
- 天龙八部按钮精灵脚本是什么工作原理安全吗?
- 造梦西游3悟空流沙河怎么打 造梦西游3悟空流沙河打法攻略
- 造梦西游3 动态
- 4摩尔庄园带鱼在哪钓摩尔庄园带鱼怎么钓摩尔庄园带鱼浆果火候
- 洛克王国VIP神殿在哪 怎么去
- 摩尔庄园手游精灵分布位置图
- 神鬼战士HD 不能登入
- 卡兹克全攻略 技巧 出装与打法详解
- 原神的遗迹是什么 《原神》神秘的遗迹任务攻略
- 神鬼战士HD 不能登入