一个游戏中的条件概率问题

假设你在进行一个游戏节目。现给三扇门供你选择:其中一扇门后面是一个大奖(比如奥迪R8),另两扇门后面神马都没有。你不是托,所以你的目的当然是拿大奖,但是你显然不知道门后面是啥东东。主持人(虽然ta知道门后面都是啥,但ta就是不告诉你)先让你做第一次选择。在你选择了一扇门后,主持人并没有立刻打开这扇门,而是打开了另外一扇木有奖的门给你看。现在,主持人告诉你,你有一次重新选择的机会。那么,请你思考一下,你是坚持第一次的选择不变,还是改变第一次的选择?哪种做法更有可能中大奖?

这个问题貌似很简单:主持人排除一扇没奖的门之前做选择是三选一,得奖的概率是1/3,而排除之后是二选一,得奖的概率是1/2,显然1/2 > 1/3,所以换了得奖的机会更大。

但其实这个算法是不完全正确的。坚持第一次的选择不变,获奖的概率的确是1/3;但是改变第一次的选择,获奖的概率不是1/2,而是2/3!

你信么?

不信的同学不妨先看看穷举法得到的结果:

A B C
Y N N
1 2 X
2 1 X
2 X 1

注释:A,B,C代表三扇不同的门;Y表示此门有大奖,N表示此门木有奖;1表示第一次的选择,2表示第二次的选择;X表示这扇门被主持人排除而不能把戏演到底。

若是坚持第一次的选择不变,那么就只看带有1的部分:在三个1里面,跟Y一列的(即中奖的)只有一个,因此获奖概率是1/3。

若是改变第一次的选择,那么就只看带有2的部分:在三个2里面,跟Y一列的有两个,因此获奖的概率是2/3,而不是1/2。

这个穷举法有个条件:里面的1不能出现在相同的行(这种情况违反逻辑),也最好不要出现在相同的列,这样才能保证每一个1的概率都相等,不然查个数算概率的基础就错了。

实际上,这是一个“条件概率”的计算。你先任意选择一扇门,每扇门被选中的概率都是1/3,然后基于你的选择这个前提条件,主持人再选择一扇门。倘若你第一次选了一扇没有奖的门,那么,主持人选择一扇没有奖的门的条件概率是1,联合概率是1/3;倘若你第一次选了一扇有奖的门,那么主持人选择一扇没有奖的门的条件概率是1/2,联合概率是1/3*1/2=1/6。所以,不换的中奖概率是1/6+1/6=1/3,换的中奖概率是1/3+1/3=2/3 。

喂神马会是这样?喂神马是2/3而不是1/2?因为除了主持人排除了一扇门这个明显的限制条件之外,还有一个隐含的限制条件:改变第一次的选择。就是说,只要你改变第一次的选择,那么你就不是在剩下的“两”扇门中间做选择,你只有唯一的一扇门可选了。这个时候,你更像是跟之前的自己作对:如果之前你的选择有1/3的概率中奖,那么此时改变选择之后,你就有1/3的概率与大奖擦肩而过;如果之前你的选择有2/3的概率落空,那么此时改变选择之后,你就有2/3的概率把大奖抱回家。(看出来这两句话是一句话的同学,恭喜你!你可以去参加这类游戏了!)

说到这里,不论你明没明白,我都要接着说。没准你再往下看一点就明白了。

如果把三扇门扩大到四扇门会是神马情况?

A B C D
Y N N N
1 2 X 2
2 1 X 2
2 X 1 2
2 2 X 1

用上述数数的方法可知,坚持第一次选择不变的获奖概率是1/4,而改变第一次的选择的获奖概率是3/8,不是1/3=3/9(喂神马这么写?且看下文**处)。很明显,主持人排除一扇门,并且你改变先前的选择之后,你只有(4-1-1)扇可以真正自由选择的门(而不是3扇),它们的概率相等,都是1/(4-1-1)=1/2。若想获奖,则你的第一次选择必须是错误的、没奖的,这个概率为(1-1/4)=3/4。因此,改变第一次的选择并且获奖的概率是(3/4)*(1/2)=3/8。

若继续往下推算,五扇门的情况是:不改变选择的获奖概率是1/5,改变的获奖概率是(1-1/5)*{1/(5-1-1)}=4/15,而不是1/4=4/16。

六扇门的情况是……你自己去问郭芙蓉她爹郭巨侠吧!

另外,你不用担心到27扇门的时候没有英文字母来代表门(注意,我没提代表们),因为当你研究到14扇门的时候,你会发现,原来这就是N扇门的情况啊!这对于试图用上述穷举法继续往下研究的同学来说真是个福音啊!

用数学归纳法可证得(不需要其它的高深数学技能,有性趣的同学可用手自行解决),N扇门的情况是,坚持不变的中奖概率是1/N,改变的中奖概率是(1-1/N)*{1/(N-1-1)}=(N-1)/{N*(N-2)}。其中,N是大于等于3的正整数。(**处)这里另外的一种归纳是:(N-1)/{(N-1)^2-1)}=(N-1)/{N*(N-2)}。

显然,(N-1)/(N-2)>1,所以改变第一次的选择而中奖的概率大于坚持第一次选择的中奖概率。因此,以后你参加这种犯贱的游戏时,原则就是一定要换!

说到这里,如果你还没明白,请回到六扇门那儿重新来过。

把上面的问题再推广一点,我们可以得到一个更平凡的结论:

给你N扇门,其中m个是有奖的,其余没奖,你先选一扇,然后主持人打开另外L扇没有奖的给你看,再让你重新选一扇门。此时,坚持不换门而获奖的概率是m/N;换另外一扇门而获奖的概率是{m*(N-m)}/{N*(N-L-1)}。其中,N,m,L均为正整数,且N>2,(m+L)<N。

怎么得到的?可以这样理解:显然第一次选择的获奖概率是m/N,那么改变第一次的选择意味着抛弃了这m*(m/N)个大奖,还剩下{m-m*(m/N)}个大奖。你要在(N-L-1)个剩下的门中选一个,这时的中奖概率自然是{m-m*(m/N)}/(N-L-1)={m*(N-m)}/{N*(N-L-1)}。

这种情况下,究竟怎样才能获奖就要看(L+1)和m谁大了。当L+1比m大时,改变先前选择而获奖的概率更大;反之,当L+1比m小时,坚持原来的选择中奖概率更高;若L+1=m,则换不换无所谓,概率是相等的。

最后,我再说一点(你能坚持到这里表明你真的很理性),这里还可以再引申出一个更更平凡的推论,即第一次可以选择k扇门,而第二次可以选择j扇门,其中k+j+L<N。

则坚持第一次选择不变的中奖概率是:

1-(1-m/N)^k

而改变第一次选择的中奖概率是:

1-{1-m*z/(N-L-k)}^j,其中,z=(1-m/N)^k

这个时候到底是换还是不换?who特么knows!

好了说了这么多了,喝杯水捋一捋思路,这不口说无凭,要不上个代码测试一下:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

//有三扇门,其中一扇门里有奖品,三选一,你选择其中一扇门之后
//主持人先不揭晓答案,而是从另外两扇门中排除掉一个没有奖品的门
//现在主持人问你,要不要换个门,请问你换还是不换?
9
	//测试(总次数, 换门)
	void test(int max, int change);

//入口函数
void main()
{
	//各自测试的次数
	int count = 20;

	//初始化随机种子
	srand((unsigned)time(NULL));

	//不换门测试
	printf("不换门测试:\n");
	test(count, 0);

	//换门测试
	printf("换门测试:\n");
	test(count, 1);
}

//测试(总次数, 换门)
void test(int max, int change)
{

	//max    次数:测试的总次数
	//change 换门: 0不换  1换门

	int i;        //循环因子
	int m;        //目标:本次中奖的目标数字
	int c;        //初选:本次选手初次选择的数字
	int x;        //选择:本次选手最终选择的数字
	int p;        //排除:主持人排除掉没奖的数字
	int z = 0;    //中奖:中奖的总次数

	//循环多次模拟换门测试
	for (i=0; i<max; i++)
	{
		m = rand()%3; //目标:本次中奖的目标数字
		c = rand()%3; //初选:本次选手初次选择的数字

		//求出主持人要排除的数字
		if(m==c)
		{
			//选手选择了一个有奖品的,主持人从另外两个没奖品当中随机排除掉一下
			p = rand()%2;  //产生  false or true
			switch(c)
			{
			case 0:  //要排除的是:2 or 1
				p = p?2:1;
				break;
			case 1:  //要排除的是:2 or 0
				p = p?2:0;
				break;
			case 2:  //要排除的是:1 or 0
				p = p?1:0;
				break;;
			}
		}
		else
		{
			//选手选择了一个没奖品的,主持人排除另一个没奖品的
			//3-(m+c) = p
			//3-(0+1) = 2
			//3-(0+2) = 1
			//3-(1+2) = 0
			p = 3-(m+c);
		}

		//决定终选
		if(change)
		{//换门
			//x=3-(p+c)
			//x=3-(0+1) = 2
			//x=3-(0+2) = 1
			//x=3-(1+2) = 0
			x = 3-(p+c);  //换个门
		}
		else
		{//不换门
			x = c;  //最终选择和初次选择一样
		}

		//结果
		printf("第%02d次  初选的是:%d, 目标是:%d, 排除的是:%d, 终选的是:%d", i+1, c, m, p, x);
		if(m==x)
		{
			//中奖了
			z++;
			printf("  (中奖了)\n");
		}
		else
		{
			//没中奖
			printf("\n");
		}
	}
	//输出结果
	printf("进行%d次测试,中奖%d次。\n\n", max, z);
}

另外,当需要进行1000次测试时,就不适合将每一次测试的结果都进行输出了,代码稍微改一下,将C语言的代码发给大家看看

#include <stdio.h>
#include <time.h>
#include <stdlib.h>

//有三扇门,其中一扇门里有奖品,三选一,你选择其中一扇门之后
//主持人先不揭晓答案,而是从另外两扇门中排除掉一个没有奖品的门
//现在主持人问你,要不要换个门,请问你换还是不换?
////测试(总次数, 换门)
void test(int max, int change);

//入口函数
void main()
{
    //各自测试的次数
    int i, count = 1000;

    //初始化随机种子
    srand((unsigned)time(NULL));

    for(i=0;i<5;i++)
    {
        printf("================================\n");

        //不换门测试
        printf("不换门测试:\n");
        test(count, 0);

        //换门测试
        printf("换门测试:\n");
        test(count, 1);
    }
}

//测试(总次数, 换门)
void test(int max, int change)
{

    //max    次数:测试的总次数
    //change 换门: 0不换  1换门

    int i;        //循环因子
    int m;        //目标:本次中奖的目标数字
    int c;        //初选:本次选手初次选择的数字
    int x;        //选择:本次选手最终选择的数字
    int p;        //排除:主持人排除掉没奖的数字
    int z = 0;    //中奖:中奖的总次数

    //循环多次模拟换门测试
    for (i=0; i<max; i++)
    {
        m = rand()%3; //目标:本次中奖的目标数字
        c = rand()%3; //初选:本次选手初次选择的数字

        //求出主持人要排除的数字
        if(m==c)
        {
            //选手选择了一个有奖品的,主持人从另外两个没奖品当中随机排除掉一下
            p = rand()%2;  //产生  false or true
            switch(c)
            {
            case 0:  //要排除的是:2 or 1
                p = p?2:1;
                break;
            case 1:  //要排除的是:2 or 0
                p = p?2:0;
                break;
            case 2:  //要排除的是:1 or 0
                p = p?1:0;
                break;;
            }
        }
        else
        {
            //选手选择了一个没奖品的,主持人排除另一个没奖品的
            //3-(m+c) = p
            //3-(0+1) = 2
            //3-(0+2) = 1
            //3-(1+2) = 0
            p = 3-(m+c);
        }

        //决定终选
        if(change)
        {//换门
            //x=3-(p+c)
            //x=3-(0+1) = 2
            //x=3-(0+2) = 1
            //x=3-(1+2) = 0
            x = 3-(p+c);  //换个门
        }
        else
        {//不换门
            x = c;  //最终选择和初次选择一样
        }

        //结果
        //printf("第%02d次  初选的是:%d, 目标是:%d, 排除的是:%d, 终选的是:%d", i+1, c, m, p, x);
        if(m==x)
        {
            //中奖了
            z++;
            //printf("  (中奖了)\n");
        }
        else
        {
            //没中奖
            //printf("\n");
        }
    }
    //输出结果
    printf("进行%d次测试,中奖%d次。\n\n", max, z);
}

时间: 2024-08-07 14:26:00

一个游戏中的条件概率问题的相关文章

游戏中逻辑线程和逻辑线程的并行

为什么要将游戏的渲染线程和逻辑线程分离? 游戏中渲染是一个非常耗时的操作,特别是相对复杂的游戏,渲染通常会占据一帧中的大部分时间.而高品质的游戏都会要求FPS在60,所以一帧的时间仅仅16毫秒. 如果要在16毫秒内完成逻辑和渲染双重的任务,对于大型游戏来说,通常是艰难的,即使在极度优化的情况下,也可能只能满足性能较好的设备,在性能较差的设备上,几乎不可能在16毫秒内完成所有任务. 所以如果将渲染线程和逻辑线程分离,那么理论上,他们各自都有16毫秒的时间来完成各自的任务,因为他们是并行进行的,这样

游戏中的设计模式(1)--观察者模式

作为本系列文章的第一篇,笔者在此想表达一下个人对于设计模式的一些理解.因此笔者自问自答几个问题.1,什么是设计模式?2,在软件快速迭代的今天,设计模式是否重要?3,四人帮提出了23个设计模式,是不是设计问题无出其右?4,设计模式和语言是什么关系?5,设计模式和面向对象的关系? 1,什么是设计模式? 拿百度谷歌一下,可以找到各种各样的回答,只是很多的回答实在是会让人找不到北,模式这个词确实很高大上,因此常常出没于学术界.针对设计模式这个问题,笔者对设计模式的理解是,设计模式可以认为是解决某一类问题

js之翻牌游戏中的一个深刻感悟

先“上菜”: 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>记忆方块</title> 6 <script src="randomNum.js"></script> 7 <script src="Card.js"&g

如何在cocos2d-x中使用ECS(实体-组件-系统)架构方法开发一个游戏?

引言 在我的博客中,我曾经翻译了几篇关于ECS的文章.这些文章都是来自于Game Development网站.如果你对这个架构方式还不是很了解的话,欢迎阅读理解 组件-实体-系统和实现 组件-实体-系统. 我发现这个架构方式,是在浏览GameDev上的文章的时候了解到的.很久以前,就知道了有这么个架构方法,只是一直没有机会自己实践下.这一次,我就抽空,根据网上对ECS系统的讨论,采用了一种实现方法,来实现一个. 我很喜欢做游戏,所以同样的,还是用游戏实例来实践这个架构方法.我将会采用cocos2

最近一个项目中关于NGUI部分的总结

最近一个项目中关于NGUI部分的总结           在自己最近的一个项目中,软件的界面部分使用了NGUI来进行制作.在制作过程中,遇到了一些问题,也获取了一些经验,总结下来,作为日后的积累. 1.NGUI图集的使用. 此次是第一个自己正儿八经的制作完整图集的项目,感受颇深.在使用NGUI制作界面时,图集的选用是一个关键,因为它直接关系到了drawcall的数量.最好就是自始至终都只使用同一个图集中的元素,这样的话,在界面制作上drawcall的消耗就只会受到Panel的划分以及字体与图集的

Dota 游戏中的攻击与伤害分析

摘要:在上一篇文章中分析了物理攻击和护甲的攻防分析,但是忽略了英雄对战里面一个很重要的角色--技能攻击.实际上,除了少数后期英雄可以直接靠平砍(即物理攻击)杀人外,大部分英雄尤其是智力英雄还是要靠技能收割人头的.技能的使用也是评价一个玩家水平高低的主要指标.在本文中,我们就技能进行分析. 关键字:技能攻击 魔抗 护甲 伤害类型 攻击类型 Dota中的攻击类型共有普通攻击.穿刺攻击.攻城攻击.混乱攻击.英雄攻击和法术攻击6种.除了法术攻击,其他的统称为物理攻击.然而我们只考虑英雄的话,只有英雄攻击

全屏游戏中自动切出到桌面的问题解决(二)

关于全屏游戏,类似英雄联盟等,游戏中,自动切到桌面的问题,之前发布过一个解决过的问题.今天又碰到一个类似的问题,当然还是用前端进程工具查找一下什么进程引起的游戏切出.经过观察为update.exe这个进程导致.那么下面就需要查找这个文件的来源了.通过查找路径,发现此文件随机性特别强,任意变换目录,而且通过软件也看不出其父进程.如图:    那么只有借助其他软件了.打开 ProcessExplorer,观察所有进程,发现有UPDATE,但是也无法观察到父进程.那么结束掉这个进程,并且使用顺网小哥的

全屏游戏中自动切出到桌面的问题解决

近期遇到一个客户全屏游戏中,自动切出游戏到桌面的问题,于是到现场解决.     打开游戏<英雄联盟>测试,并开启前端进程监测软件监测.一段时间过后,游戏切出,发现一个名称为"DeskTopHelper.exe"的进程.如图: 好吧,那么看一看这个进程是哪个软件带的吧.打开PROCESS EXPLORER,查看一下,如图: 通过进程关系,并进一步查找文件目录得知,这个进程是"95网吧大师的进程".那么得知是哪个软件导致的,解决办法就不用再说了吧. 附上工具

游戏中的音效

1. 游戏中的即时音效: 在游戏中,根据情况播放的即时音效较为短暂.可以重复.也可以同时播放.由于Android中提供的MediaPlayer会占用大量的系统资源,而且播放时还会进行缓冲,有较大的延时,因此使用MediaPlayer无法实现即时音效的播放.而在Android中专门提供的SoundPool类主要用于管理和播放应用程序中的声音资源,使用该类时首先需要通过该类将声音资源加载到内存中,然后在需要的即时音效的地方播放即可,几乎没有延时现象.[由于SoundPool设计的初衷是用于无延时地播