蒙提霍尔的c语言程序,蒙提霍尔问题

蒙提霍尔问题

蒙提霍尔问题(Monty Hall problem),亦称为三门问题,有可能是历史上最富争议的概率问题。蒙提·霍尔是美国电视游戏节目 Let's Make a Deal 的主持人,蒙提霍尔问题就是源自该节目中的一个游戏。

這个游戏的玩法是:参赛者会看见三扇关闭的门,其中一扇门的后面有汽车,选中后面有汽车的那扇门就可以贏得该汽车。而另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,知道门后情形的节目主持人会开启剩下两扇门中的一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。

问题是:换另一扇门是否会增加参赛者贏得汽车的概率?

这个问题也被叫做蒙提霍尔悖论:虽然该问题的答案在逻辑上并不自相矛盾,但十分违反直觉。这个问题曾引起热烈的讨论。

明确的限制条件

Mueser 和 Granberg 为主持人的行为加上明确的限制条件,提出了对这个问题的一种不含糊的陈述:

参赛者在三扇门中挑选一扇。他并不知道门后是什么。

主持人知道每扇门后面有什么。

主持人必须开启剩下两扇门中的一扇,并且必须给参赛者提供换门的机会。

主持人永远都会开启一扇有山羊的门。

如果参赛者挑选了一扇有山羊的门,主持人必須开启另一扇有山羊的门。

如果参赛者挑选了一扇有汽车的门,主持人随机开启另外两扇有山羊的门中的一扇。

参赛者会被问是否保持他的原来的选择,还是转而选择剩下的那一扇门。

改变选择可以增加参赛者赢得汽车的概率吗?

计算机模拟程序

根据以上论述,我们有以下 C# 程序:

1: using System;

2:

3: namespace Skyiv.Ben.MontyHallProblem

4: {

5: sealed class Game

6: {

7: // 以下三个变量的取值范围均为 0,1,2 。

8: int car; // 汽车在哪扇门后面?

9: int choice; // 参赛者选择了哪扇门?

10: int shown; // 主持人开启了哪扇门?

11:

12: public Game(Random rand)

13: {

14: // Random.Next(3) 随机返回 0,1,2 中的一个。

15: car = rand.Next(3); // 随机安排汽车在某一扇门后面。

16: choice = rand.Next(3); // 参赛者在三扇门中随机挑选一扇。

17: shown = GetShown(rand); // 主持人开启一扇有山羊的门。

18: }

19:

20: int GetShown(Random rand)

21: {

22: // 参赛者挑选了一扇有山羊的门,主持人开启另一扇有山羊的门。

23: if (car != choice) return GetRemainDoor(choice, car);

24:

25: // 参赛者挑选了一扇有汽车的门,主持人随机开启另外两扇有山羊的门中的一扇。

26: var r = rand.Next(2); // Random.Next(2) 随机返回 0,1 中的一个。

27: if (choice == 2) return r; // 如果参赛者挑选了2号门,主持人随机开启 0,1 号门中的一扇。

28: if (choice == 0) return r + 1; // 如果参赛者挑选了0号门,主持人随机开启 1,2 号门中的一扇。

29: return r * 2; // 如果参赛者挑选了1号门,主持人随机开启 0,2 号门中的一扇。

30: }

31:

32: int GetRemainDoor(int a, int b)

33: { // 返回 a 和 b 以外的另一扇门。a, b 和返回值的取值范围均为 0,1,2,且 a != b 。

34: return 3 - a - b;

35: }

36:

37: public bool StickerWin()

38: { // 保持选择,参赛者选中有汽车的门就赢了。

39: return car == choice;

40: }

41:

42: public bool SwapperWin()

43: { // 改变选择,参赛者新的选择是另一扇尚未开启的门。

44: return car == GetRemainDoor(choice, shown);

45: }

46: }

47:

48: sealed class Tester

49: {

50: static void Main()

51: {

52: var rand = new Random();

53: int n = 100000000, stickers = 0, swappers = 0;

54: for (var i = 0; i < n; i++)

55: {

56: var game = new Game(rand);

57: if (game.StickerWin()) stickers++;

58: if (game.SwapperWin()) swappers++;

59: }

60: Console.WriteLine("测试次数: {0:N0}", n);

61: Console.WriteLine("保持选择: {0:N0} ({1:P})", stickers, stickers / (double)n);

62: Console.WriteLine("改变选择: {0:N0} ({1:P})", swappers, swappers / (double)n);

63: }

64: }

65: }

上述程序模拟玩这个游戏一亿次,计算在保持选择和改变选择两种情况下赢得汽车的概率。

运行结果

在 Arch Linux 64-bit 操作系统 Mono 3.4.0 环境下编译和运行:

$ dmcs MontyHallProblem.cs

$ mono MontyHallProblem.exe

测试次数: 100,000,000

保持选择: 33,337,906 (33.34%)

改变选择: 66,662,094 (66.66%)

$ mono MontyHallProblem.exe

测试次数: 100,000,000

保持选择: 33,339,359 (33.34%)

改变选择: 66,660,641 (66.66%)

$ mono MontyHallProblem.exe

测试次数: 100,000,000

保持选择: 33,327,813 (33.33%)

改变选择: 66,672,187 (66.67%)

$ mono MontyHallProblem.exe

测试次数: 100,000,000

保持选择: 33,330,327 (33.33%)

改变选择: 66,669,673 (66.67%)

$ mono MontyHallProblem.exe

测试次数: 100,000,000

保持选择: 33,337,882 (33.34%)

改变选择: 66,662,118 (66.66%)

从上面五次运行结果看,保持选择赢得汽车的概率是 1/3,而改变选择赢得汽车的概率是 2/3 。

如果主持人不知道奖品在哪里

《统计思维:程序员数学之概率统计》第 5 章第 2 节“蒙提霍尔问题”的最后:

习题5-5

理解蒙提霍尔问题的重点在于要明白蒙提打开一扇门实际上是给你提供了信息。想象一下,如果蒙提不知道奖品在哪里,随机打开B门或C门,会怎么样?

如果打开的门后是汽车,游戏结束,你输了,再没有选择的机会。否则,你是应该坚持还是改变选择?

要解这道题,只要删除上述 C# 程序第 22 至 25 行,重新编译和运行:

$ dmcs MontyHallProblem.cs && mono MontyHallProblem.exe

测试次数: 100,000,000

保持选择: 33,335,483 (33.34%)

改变选择: 33,330,986 (33.33%)

可见在此情况下,坚持和改变选择是一样的,都只有 1/3 的概率赢得奖品。

参考资料

参考资料[1]提供了详细全面的内容。参考资料[3]的评论很值得一看。

ee4aae9583c4edf47d9931fd19b53df0.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
下面是一个使用C语言编写霍尔电机反馈程序的示例: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #define MAX_SPEED 100 // 最大速度限制 #define MIN_SPEED 0 // 最小速度限制 typedef struct { int encoderCount; // 编码器计数 int targetCount; // 目标计数 float kp; // 比例系数 float ki; // 积分系数 float kd; // 微分系数 float lastError; // 上次误差 float integral; // 积分项 } Motor; void motorInit(Motor* motor, int targetCount, float kp, float ki, float kd) { motor->encoderCount = 0; motor->targetCount = targetCount; motor->kp = kp; motor->ki = ki; motor->kd = kd; motor->lastError = 0; motor->integral = 0; } float calculatePID(Motor* motor) { int error = motor->targetCount - motor->encoderCount; float proportional = motor->kp * error; motor->integral += motor->ki * error; float derivative = motor->kd * (error - motor->lastError); motor->lastError = error; float output = proportional + motor->integral + derivative; // 限制输出在最大和最小速度之间 output = fmaxf(fminf(output, MAX_SPEED), MIN_SPEED); return output; } void updateEncoderCount(Motor* motor, int count) { motor->encoderCount = count; } int main() { Motor motor; motorInit(&motor, 1000, 0.5, 0.1, 0.2); int encoderCount = 500; updateEncoderCount(&motor, encoderCount); float output = calculatePID(&motor); printf("Motor output: %.2f\n", output); return 0; } ``` 以上代码是一个简单的PID控制器的实现示例。你可以根据具体的硬件情况和需求进行修改和扩展。需要注意的是,以上示例只是一个基础的框架,具体的PID参数调节和编码器计数更新需要根据你的实际情况进行调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值