三门问题模拟对比试验——附VB6和Python3源程序及EXE文件
阅读原文时间:2023年07月10日阅读:2

三门问题模拟对比试验

【直接跳转到文件下载】

前言

“三门问题”是一个很有意思的概率论问题,涉及贝叶斯公式,是人工智能领域的一个经典问题。

为了直观地研究这个问题,也为了测试一下Python的性能,阿色用VB6和Python3分别编制模拟试验程序,并将源代码和EXE文件公布。

VB6和Python3采用了相同的算法,并使用同一台电脑测试。

希望对大家有所帮助。

1 什么是“三门问题”?

三门问题(Monty Hall Problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let's Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。

参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。

当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。

主持人其后会问参赛者要不要换另一扇仍然关上的门。

问题是:换另一扇门会否增加参赛者赢得汽车的机率。

如果严格按照上述的条件,即主持人清楚地知道,自己打开的那扇门后是羊,那么答案是会。

不换门的话,赢得汽车的几率是1/3。换门的话,赢得汽车的几率是2/3。

虽然该问题的答案在逻辑上并不自相矛盾,但十分违反直觉。这问题曾引起一阵热烈的讨论。

(摘自百度百科)

2 VB6模拟试验

2.1 试验结果

下列各图是利用Visual Basic 6 实现,始终采取“不换”或“”策略、是“否动态显示”为条件的模拟情况。

动态显示耗时较多,为了客观对比,特别区分了“纯计算”和“动态显示”的情况。

纯计算试验1000000次,动态显示试验1000次。

试验总体情况:

(1)得车概率

采取“不换”策略,得车概率是1/3(约33%);

采取“换”策略,得车概率是2/3(约67%)。

(2)模拟计算用时

纯计算1000000次,“不换”和“换”策略基本相同:4秒;

动态显示计算1000次,“不换”和“换”策略基本相同:61~62秒。

2.2 VB6源代码

源代码如下(因排版等问题,请以附后的下载文件包为准):

(1)三门问题试验.vbw
Form1 = 0, 0, 1308, 803, , 26, 26, 1005, 569, C

(2)三门问题试验.vbp
Type=Exe
Form=三门问题试验.frm
Reference=*\G{00020430-0000-0000-C000-000000000046}#2.0#0#C:\Windows\SysWOW64\stdole2.tlb#OLE Automation
IconForm="Form1"
Startup="Form1"
HelpFile=""
Title="三门问题试验"
ExeName32="三门问题试验.exe"
Command32=""
Name="三门问题试验"
HelpContextID="0"
CompatibleMode="0"
MajorVer=1
MinorVer=0
RevisionVer=0
AutoIncrementVer=0
ServerSupportFiles=0
VersionCompanyName="大系统观开放论坛 BSV"
VersionLegalCopyright="开源免费"
VersionProductName="三门问题模拟试验"
CompilationType=0
OptimizationType=0
FavorPentiumPro(tm)=0
CodeViewDebugInfo=0
NoAliasing=0
BoundsCheck=0
OverflowCheck=0
FlPointCheck=0
FDIVCheck=0
UnroundedFP=0
StartMode=0
Unattended=0
Retained=0
ThreadPerObject=0
MaxNumberOfThreads=1

(3)三门问题试验.frm
VERSION 5.00
Begin VB.Form Form1
AutoRedraw = -1 'True
BackColor = &H00FFFFFF&
BorderStyle = 1 'Fixed Single
Caption = "三门问题模拟试验对比 大系统观-阿色/2020.11"
ClientHeight = 9285
ClientLeft = 45
ClientTop = 390
ClientWidth = 17055
Icon = "三门问题试验.frx":0000
LinkTopic = "Form1"
MaxButton = 0 'False
MinButton = 0 'False
ScaleHeight = 9285
ScaleWidth = 17055
StartUpPosition = 3 '窗口缺省
Begin VB.TextBox TXT_Computing
Alignment = 2 'Center
BackColor = &H00C0FFC0&
BorderStyle = 0 'None
BeginProperty Font
Name = "宋体"
Size = 48
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 975
Left = 1920
TabIndex = 28
Text = "正在计算…"
Top = 3960
Visible = 0 'False
Width = 5895
End
Begin VB.CheckBox CHK_DynamicShow
BackColor = &H00FFFFFF&
Caption = "显示动态-影响速度,最多1000次"
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 255
Left = 5160
TabIndex = 25
Top = 7680
Width = 3975
End
Begin VB.CommandButton BT_SourceCode
Caption = "查看源码/下载"
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 975
Left = 6960
TabIndex = 3
Top = 8040
Width = 2175
End
Begin VB.TextBox TXT_Stat
Height = 1000
Left = 4800
MultiLine = -1 'True
TabIndex = 17
TabStop = 0 'False
Text = "三门问题试验.frx":424A
Top = 2520
Width = 4335
End
Begin VB.TextBox TXT_TestTimes
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 285
Left = 1440
TabIndex = 4
Text = "1000000"
Top = 7680
Width = 1095
End
Begin VB.TextBox TXT_SingleData
Height = 5055
Left = 360
MultiLine = -1 'True
ScrollBars = 3 'Both
TabIndex = 7
TabStop = 0 'False
Text = "三门问题试验.frx":42D0
Top = 2520
Width = 4335
End
Begin VB.CommandButton BT_Quit
Caption = "不玩了"
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 975
Left = 4800
TabIndex = 2
Top = 8040
Width = 2175
End
Begin VB.CommandButton BT_Change
Caption = "每次都换"
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 975
Left = 2520
TabIndex = 1
Top = 8040
Width = 2175
End
Begin VB.CommandButton BT_NoChange
Caption = "每次都不换"
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 975
Left = 360
TabIndex = 0
Top = 8040
Width = 2175
End
Begin VB.Label Label13
Alignment = 2 'Center
Appearance = 0 'Flat
AutoSize = -1 'True
BackColor = &H80000005&
Caption = "完成次数"
BeginProperty Font
Name = "宋体"
Size = 15.75
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
ForeColor = &H80000008&
Height = 315
Left = 6360
TabIndex = 27
Top = 3840
Width = 1335
End
Begin VB.Label LB_TimesFinished
Alignment = 2 'Center
BackColor = &H00C0C0FF&
Caption = "1000000"
BeginProperty Font
Name = "宋体"
Size = 36
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 705
Left = 5160
TabIndex = 26
Top = 4200
Width = 3615
End
Begin VB.Label Label6
Alignment = 2 'Center
BackStyle = 0 'Transparent
Caption = "大系统观指导人工智能"
BeginProperty Font
Name = "宋体"
Size = 15
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
ForeColor = &H00008000&
Height = 495
Left = 10320
TabIndex = 24
Top = 8640
Width = 4815
End
Begin VB.Image Image3
Height = 2055
Left = 14900
Picture = "三门问题试验.frx":42F7
Stretch = -1 'True
Top = 6840
Width = 1840
End
Begin VB.Line Line2
X1 = 3000
X2 = 6600
Y1 = 840
Y2 = 840
End
Begin VB.Line Line1
BorderColor = &H00C0C0C0&
X1 = 9480
X2 = 9480
Y1 = 240
Y2 = 9000
End
Begin VB.Label LB_Strategy
Alignment = 2 'Center
BackColor = &H00C0C0FF&
Caption = "不换"
BeginProperty Font
Name = "宋体"
Size = 18
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 375
Left = 7560
TabIndex = 23
Top = 1320
Width = 1575
End
Begin VB.Label Label16
BackStyle = 0 'Transparent
Caption = "你的策略:"
BeginProperty Font
Name = "宋体"
Size = 18
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 375
Left = 5760
TabIndex = 22
Top = 1320
Width = 2055
End
Begin VB.Label LB_Lang
Alignment = 2 'Center
BackColor = &H00C0C0FF&
Caption = "VB6"
BeginProperty Font
Name = "宋体"
Size = 18
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 375
Left = 2160
TabIndex = 21
Top = 1320
Width = 1455
End
Begin VB.Label Label14
BackStyle = 0 'Transparent
Caption = "编程语言:"
BeginProperty Font
Name = "宋体"
Size = 18
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 375
Left = 360
TabIndex = 20
Top = 1320
Width = 2055
End
Begin VB.Label LB_TimeLen
Alignment = 2 'Center
BackColor = &H00C0C0FF&
Caption = "333470"
BeginProperty Font
Name = "宋体"
Size = 36
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 705
Left = 5160
TabIndex = 19
Top = 6840
Width = 3615
End
Begin VB.Label Label11
Alignment = 2 'Center
Appearance = 0 'Flat
AutoSize = -1 'True
BackColor = &H80000005&
Caption = "计算用时(秒)"
BeginProperty Font
Name = "宋体"
Size = 15.75
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
ForeColor = &H80000008&
Height = 315
Left = 6015
TabIndex = 18
Top = 6480
Width = 2025
End
Begin VB.Label Label10
BackStyle = 0 'Transparent
Caption = "(单位:次)"
Height = 255
Left = 8280
TabIndex = 16
Top = 2040
Width = 975
End
Begin VB.Label Label9
BackStyle = 0 'Transparent
Caption = "1号门 2号门 3号门 合计"
BeginProperty Font
Name = "宋体"
Size = 9
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 255
Left = 6240
TabIndex = 15
Top = 2280
Width = 3255
End
Begin VB.Label Label8
Alignment = 2 'Center
BackStyle = 0 'Transparent
Caption = "单次试验数据"
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 375
Left = 960
TabIndex = 14
Top = 1920
Width = 2895
End
Begin VB.Label Label7
Alignment = 2 'Center
BackColor = &H00E0E0E0&
BackStyle = 0 'Transparent
Caption = "综合统计结果"
BeginProperty Font
Name = "宋体"
Size = 24
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 615
Left = 2760
TabIndex = 13
Top = 360
Width = 4095
End
Begin VB.Label LB_Prob
Alignment = 2 'Center
BackColor = &H00C0C0FF&
Caption = "33.33%"
BeginProperty Font
Name = "宋体"
Size = 36
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 705
Left = 5160
TabIndex = 12
Top = 5520
Width = 3615
End
Begin VB.Label Label5
Alignment = 2 'Center
Appearance = 0 'Flat
AutoSize = -1 'True
BackColor = &H80000005&
Caption = "得车概率"
BeginProperty Font
Name = "宋体"
Size = 15.75
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
ForeColor = &H80000008&
Height = 315
Left = 6360
TabIndex = 11
Top = 5160
Width = 1335
End
Begin VB.Label LB_SummaryChangge
Alignment = 2 'Center
BackStyle = 0 'Transparent
Caption = "数据统计"
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 375
Left = 5640
TabIndex = 10
Top = 1920
Width = 2655
End
Begin VB.Label Label4
BackStyle = 0 'Transparent
Caption = "序号 车在门号 选门号 排除门号 换否 结果"
BeginProperty Font
Name = "宋体"
Size = 9
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 255
Left = 360
TabIndex = 9
Top = 2280
Width = 4455
End
Begin VB.Label Label3
BackStyle = 0 'Transparent
Caption = "试验次数 *最多1000000次"
BeginProperty Font
Name = "宋体"
Size = 12
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 495
Left = 360
TabIndex = 8
Top = 7680
Width = 4695
End
Begin VB.Label Label2
Alignment = 2 'Center
BackStyle = 0 'Transparent
Caption = "三 门 问 题"
BeginProperty Font
Name = "宋体"
Size = 24
Charset = 134
Weight = 700
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 495
Left = 10560
TabIndex = 6
Top = 360
Width = 5295
End
Begin VB.Image Image2
BorderStyle = 1 'Fixed Single
Height = 2220
Left = 9840
Picture = "三门问题试验.frx":1567F
Stretch = -1 'True
Top = 1080
Width = 6840
End
Begin VB.Label Label1
BackColor = &H00FFFFFF&
Caption = $"三门问题试验.frx":2E53E
BeginProperty Font
Name = "宋体"
Size = 10.5
Charset = 134
Weight = 400
Underline = 0 'False
Italic = 0 'False
Strikethrough = 0 'False
EndProperty
Height = 3255
Left = 9840
TabIndex = 5
Top = 3480
Width = 6855
End
Begin VB.Image Image1
Height = 1770
Left = 9720
Picture = "三门问题试验.frx":2E78B
Stretch = -1 'True
Top = 6960
Width = 5130
End
End
Attribute VB_Name = "Form1"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = True
Attribute VB_Exposed = False

'************************************************************** 三门问题模拟验证 ***************************************************************
'
' 三门问题是人工智能公司面试时经常提问的问题。它是关于贝叶斯公式的概率论问题。这个仿真模拟验证有助于理解。
'
' 【三门问题描述】
' 这是个猜奖游戏,游戏规则和过程:
' 1、在台上放3扇门,关着。
' 2、主持人随机地分别在门后面放两只羊和一台车。
' 3、你选一扇门,门后的奖品归你。把你的选择告诉主持人。
' 4、主持人在剩下的两扇门中打开一扇后面是羊的门。
' 5、主持人对你说:我已经帮你排除了一扇有羊的门。现在剩下两扇门了。你现在可以选择是否换掉你原来的选择。
' 6、不论你换不换,你所选择的门后的奖品都归你。
'
' 【问题】为了得到车,你换不换?——试试再说!
'
'*************************************************************************************************************************************************

'********************************************************************* 定义与声明 ****************************************************************
Const PROG_LANG = "VB6" '编程语言,对比VB和Python
Const MAX_TEST_TIMES = 1000000 '最大试验次数,如果显示动态,则为1000次,因为动态显示非常耗时
Const MAX_TEST_TIMES_DYNAMIC = 1000

Dim Door(3) As Integer '定义3扇门。 Door(i) = 0:羊,1:车,2:被主持人打开(羊)
Dim TotalTestTimes As Long '试验总次数
Dim CarNo, SheepNo, YouSelectNo, HostOpenNo As Integer '车所在门号,羊所在门号,你选择的门号,猜对(得车)次数
Dim TimesCarDoor(3), TimesYouSelectDoor(3), TimesHostOpenDoor(3) As Long '车在各个门的总次数,你选择各个门的总次数,主持人打开各个门的总次数
Dim TimesGetCar, TimesGetSheep As Long '得到车的总次数,得到羊的总次数
Dim Start, Finish As Single '计时
Dim OneRecord As String '单次试验数据记录

Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" (ByVal hwnd As Long, _
ByVal lpoperation As String, ByVal lpfile As String, ByVal lpparameters As String, _
ByVal lpdirectory As String, ByVal nshowcmd As Long) As Long
'******************************************************************* 定义与声明结束 **************************************************************

'****************************************************************** 程序与函数 *******************************************************************

Private Sub BT_NoChange_Click() '每次都不换
Test_Exe ("不换")
End Sub

Private Sub BT_Change_Click() '每次都换
Test_Exe ("换")
End Sub

Private Sub Test_Exe(ChangeOrNot As String)

Dim i As Long
Dim tmpstr As String
Dim i_ShowStep As Long '当试验次数很大时,间隔记录每一次实验数据,否则文本框装不下,系统有限制,1000条左右记录较好

Init_Data '初始化数据
LB_Strategy.Caption = ChangeOrNot
If CHK_DynamicShow.Value = 0 Then TXT_Computing.Visible = True
tmpstr = ""
i_ShowStep = TotalTestTimes / 1000
If i_ShowStep < 1 Then i_ShowStep = 1

'=============================== 开始多次模拟试验 ===============================

For i = 1 To TotalTestTimes

'****************************** 开始一次试验 *****************************

'初始化三扇门
Set_3_Doors (i)

'你的第一次选择,你随机选一扇门
YouSelectNo = YouFirstSelect()

'主持人打开一扇有羊的门
HostOpenNo = HostOpenADoor

'你换不换
If ChangeOrNot = "不换" Then
YouChangeOrNot (False)
Else
YouChangeOrNot (True)
End If

'揭晓
Show_Result

'是否动态显示数据
If CHK_DynamicShow.Value = 1 Then
Show_SingleData (i)
Else
If i Mod i_ShowStep = 0 Then tmpstr = tmpstr & OneRecord & vbCrLf '不动态显示时,间隔保存单次试验记录,不间隔的话数据太大,文本框装不下
End If

'****************************** 一次试验结束 *****************************

'交出控制权
DoEvents

Next i

'=============================== 多次模拟试验结束 ===============================

'不动态显示时,最后显示单次试验记录
If tmpstr <> "" Then
TXT_SingleData.Text = tmpstr
End If
'显示多次试验的数据统计
Show_Stat (i - 1)

TXT_Computing.Visible = False

End Sub

Private Sub BT_Quit_Click() '结束本程序
End
End Sub

Private Sub BT_SourceCode_Click()
t = ShellExecute(Me.hwnd, "open", "https://www.cnblogs.com/BigSystemsView/p/13901753.html", "", "", 0)
End Sub

Private Sub Form_Load()
Init_Data
End Sub

Private Sub Init_Data() '初始化数据
Randomize
TXT_SingleData.Text = ""
TXT_Stat.Text = ""
LB_Lang.Caption = PROG_LANG
LB_Strategy.Caption = ""
LB_TimesFinished.Caption = ""
LB_Prob.Caption = ""
LB_TimeLen.Caption = ""
For i = 1 To 3
TimesCarDoor(i) = 0
TimesYouSelectDoor(i) = 0
TimesHostOpenDoor(i) = 0
Next i
TimesGetCar = 0
TimesGetSheep = 0
TotalTestTimes = CLng(TXT_TestTimes.Text)
If CHK_DynamicShow.Value = 0 Then '根据是否显示动态,设定最大实验次数
If TotalTestTimes > MAX_TEST_TIMES Then
TotalTestTimes = MAX_TEST_TIMES
TXT_TestTimes.Text = TotalTestTimes
End If
Else
If TotalTestTimes > MAX_TEST_TIMES_DYNAMIC Then
TotalTestTimes = MAX_TEST_TIMES_DYNAMIC
TXT_TestTimes.Text = TotalTestTimes
End If
End If
Start = Timer
End Sub

Private Sub Set_3_Doors(i As Long) '每一次游戏开始时,设置三扇门,放置车和羊
For j = 1 To 3 '放置三扇门,均放置羊
Door(j) = 0 '0代表羊
Next j
CarNo = Int(Rnd() * 3) + 1 '主持人随机地把一扇门后面的羊换成车
Door(CarNo) = 1 '1代表车
TimesCarDoor(CarNo) = TimesCarDoor(CarNo) + 1 '统计每个门放置车的总次数
OneRecord = CStr(i) & vbTab & CarNo & vbTab '记录每一次的情况
End Sub

Function YouFirstSelect() As Integer '你第一次选择,返回选择的门号
Dim t_YouSelectNo As Integer
t_YouSelectNo = Int(Rnd() * 3) + 1 '你随机选一扇门
TimesYouSelectDoor(t_YouSelectNo) = TimesYouSelectDoor(t_YouSelectNo) + 1 '统计每个门被选择的总次数
OneRecord = OneRecord & t_YouSelectNo & vbTab '记录每一次选择
YouFirstSelect = t_YouSelectNo
End Function

Private Function HostOpenADoor() As Integer '主持人打开一扇有羊的门
Dim t_HostOpenNo As Integer
For j = 1 To 3
If j <> YouSelectNo And Door(j) = 0 Then '主持人打开你没选且有羊的任何一个门,可以取小号的门
t_HostOpenNo = j
TimesHostOpenDoor(j) = TimesHostOpenDoor(j) + 1
Exit For
End If
Next j
OneRecord = OneRecord & t_HostOpenNo & vbTab '记录主持人每次打开的门号
HostOpenADoor = t_HostOpenNo
End Function

Private Sub YouChangeOrNot(YesOrNot As Boolean) '你是否换掉原来的选择
If YesOrNot = True Then
OneRecord = OneRecord & "换" & vbTab
Select Case YouSelectNo
Case 1
If HostOpenNo = 2 Then
YouSelectNo = 3
Else
YouSelectNo = 2
End If
Case 2
If HostOpenNo = 1 Then
YouSelectNo = 3
Else
YouSelectNo = 1
End If
Case 3
If HostOpenNo = 1 Then
YouSelectNo = 2
Else
YouSelectNo = 1
End If
End Select
Else
OneRecord = OneRecord & "不换" & vbTab
End If
End Sub

Private Sub Show_Result() '揭晓本次结果
If Door(YouSelectNo) = 1 Then
OneRecord = OneRecord & "车*"
TimesGetCar = TimesGetCar + 1
Else
OneRecord = OneRecord & "羊"
TimesGetSheep = TimesGetSheep + 1
End If
End Sub

Private Sub Show_SingleData(i As Long) '显示单次试验数据记录
TXT_SingleData.Text = TXT_SingleData.Text & OneRecord & vbCrLf
TXT_SingleData.SelStart = Len(TXT_SingleData.Text)
If i Mod 10 = 0 Then '每10次刷新一下显示
Show_Stat (i)
End If
End Sub

Private Sub Show_Stat(i As Long) '显示数据统计
TXT_Stat.Text = "汽车所在" & vbTab & TimesCarDoor(1) & vbTab & TimesCarDoor(2) & vbTab & TimesCarDoor(3) & vbTab _
& (TimesCarDoor(1) + TimesCarDoor(2) + TimesCarDoor(3)) & vbCrLf
TXT_Stat.Text = TXT_Stat.Text & "第一次选" & vbTab & TimesYouSelectDoor(1) & vbTab & TimesYouSelectDoor(2) & vbTab _
& TimesYouSelectDoor(3) & vbTab & (TimesYouSelectDoor(1) + TimesYouSelectDoor(2) + TimesYouSelectDoor(3)) & vbCrLf
TXT_Stat.Text = TXT_Stat.Text & "主持人打开" & vbTab & TimesHostOpenDoor(1) & vbTab & TimesHostOpenDoor(2) & vbTab _
& TimesHostOpenDoor(3) & vbTab & (TimesHostOpenDoor(1) + TimesHostOpenDoor(2) + TimesHostOpenDoor(3)) & vbCrLf
TXT_Stat.Text = TXT_Stat.Text & "得车次数" & vbTab & vbTab & vbTab & vbTab & TimesGetCar & vbCrLf
TXT_Stat.Text = TXT_Stat.Text & "得羊次数" & vbTab & vbTab & vbTab & vbTab & TimesGetSheep & vbCrLf
LB_TimesFinished.Caption = i
LB_Prob.Caption = Format(TimesGetCar * 100 / i, "##0.00") & "%"
Finish = Timer
LB_TimeLen.Caption = CStr(Int(Finish - Start))
End Sub

'**************************************************************** 程序与函数结束 *****************************************************************

3 Python模拟试验

3.1 试验结果

下列各图是利用Python3 实现,PyCharm环境编码调试,PyInstaller编译,始终采取“不换”或“”策略、是“否动态显示”为条件的模拟情况。

动态显示耗时较多,为了客观对比,特别区分了“纯计算”和“动态显示”的情况。

纯计算试验1000000次,动态显示试验1000次。

试验总体情况:

(1)得车概率

采取“不换”策略,得车概率是1/3(约33%);

采取“换”策略,得车概率是2/3(约67%)。

(2)模拟计算用时

纯计算1000000次,“不换”和“换”策略基本相同:5秒;

动态显示计算1000次,“不换”和“换”策略基本相同:37秒。

3.2 Python3源代码

源代码如下(因排版等问题,请以附后的下载文件包为准):

(1)三门问题试验20201110.py

PROG_LANG = "Python3" # 编程语言,对比VB和Python
MAX_TEST_TIMES = 1000000 # 最大试验次数,如果显示动态,则为1000次,因为动态显示非常耗时
MAX_TEST_TIMES_DYNAMIC = 1000

import tkinter as tk
from tkinter import *
import os
import time
import random
import webbrowser

class ThreeDoorsTest:
def __init__(self):
# ******************************************** 绘制GUI ******************************************
window = tk.Tk()
window.title("三门问题模拟试验对比 大系统观-阿色/2020.11")
window.iconbitmap("三门问题试验\\BSV-LOGO.ico")
window.resizable(0, 0)
FRM_Main = tk.Frame(window)
FRM_Main.grid(row=1, rowspan=2, column=1, padx=20, sticky=S + W + E + N)
label1 = Label(FRM_Main, text='综合统计结果', font=("宋体", 24, "bold", "underline"), height=3)
label1.grid(row=1, column=1, columnspan=10)
label2 = Label(FRM_Main, text="编程语言:", font=("宋体", 18, "bold"), width=8)
label2.grid(row=2, column=1, columnspan=1, sticky=W)
label3 = Label(FRM_Main, text=PROG_LANG, width=13, font=("宋体", 18, "bold"), bg="#FFC0C0")
label3.grid(row=2, column=2, columnspan=1, sticky=W)
label4 = Label(FRM_Main, text=" 你的策略:", font=("宋体", 18, "bold"))
label4.grid(row=2, column=6, columnspan=2)
self.LB_Strategy = Label(FRM_Main, text="", width=8, font=("宋体", 18, "bold"), bg="#FFC0C0")
self.LB_Strategy.grid(row=2, column=8)
label5 = Label(FRM_Main, text="\n单次试验数据", font=("宋体", 12, "bold"), height=2)
label5.grid(row=3, column=1, columnspan=4)
label6 = Label(FRM_Main, text="\n数据统计", font=("宋体", 12, "bold"))
label6.grid(row=3, column=7, columnspan=2)
label7 = Label(FRM_Main, text="\n\n(单位:次)", font=("宋体", 10))
label7.grid(row=3, column=8, columnspan=1)
label8 = Label(FRM_Main, text="序号 车在门号 选门号 排除门号 换否 结果", font=("宋体", 10, "bold"))
label8.grid(row=4, column=1, columnspan=4)
label9 = Label(FRM_Main, text=" 1号门 2号门 3号门 合计", font=("宋体", 10, "bold"))
label9.grid(row=4, column=5, columnspan=4)

# *************************************** 显示单次试验数据 **********************************************
SCRB_SD = Scrollbar(FRM_Main) # 创建Scrollbar组件
self.TXT_SingleData = tk.Text(FRM_Main, width=44, height=29, padx=5, yscrollcommand=SCRB_SD.set)
SCRB_SD['command'] = self.TXT_SingleData.yview
SCRB_SD['width'] = 16
self.TXT_SingleData.grid(row=5, rowspan=7, column=1, columnspan=4)
SCRB_SD.grid(row=5, rowspan=7, column=5, sticky=S + W + N)
SCRB_SD.config(command=self.TXT_SingleData.yview)

# ************************************* 显示统计数据 ***************************************
self.TXT_Stat = Text(FRM_Main, width=40, height=5, padx=5)
self.TXT_Stat.grid(row=5, column=7, columnspan=2)
label9_1 = Label(FRM_Main, text="\n完成次数", font=("宋体", 16, "bold"))
label9_1.grid(row=6, column=7, columnspan=4)
self.LB_TimesFinished = Label(FRM_Main, text="", width=11, font=("宋体", 36, "bold"), bg="#FFC0C0")
self.LB_TimesFinished.grid(row=7, column=7, columnspan=4)
label10 = Label(FRM_Main, text="\n得车概率", font=("宋体", 16, "bold"))
label10.grid(row=8, column=7, columnspan=4)
self.LB_Prob = Label(FRM_Main, text="", width=11, font=("宋体", 36, "bold"), bg="#FFC0C0")
self.LB_Prob.grid(row=9, column=7, columnspan=4)
label12 = Label(FRM_Main, text="\n 计算用时(秒)", font=("宋体", 16, "bold"))
label12.grid(row=10, column=7, columnspan=4)
self.LB_TimeLen = Label(FRM_Main, text="", width=11, font=("宋体", 36, "bold"), bg="#FFC0C0")
self.LB_TimeLen.grid(row=11, column=7, columnspan=4)

# *************************************** 显示正在计算 ***************************************
self.FRM_Computing = tk.Frame(window)
self.LB_Computing = Label(self.FRM_Computing, text="正在计算…", font=("宋体", 48, "bold"), bg="#C0FFC0", height=1,
width=12)

# ************************************* 输入试验次数、按键 ***********************************
FRM_TestTimes = Frame(window)
FRM_TestTimes.grid(row=3, column=1, padx=20, pady=8, sticky=W + E)
label14 = Label(FRM_TestTimes, text="试验次数:", font=("宋体", 12))
label14.grid(row=1, column=1)
self.TotalTestTimes = IntVar(value=1000000)
self.ETY_TestTimes = Entry(FRM_TestTimes, width=10, textvariable=self.TotalTestTimes)
self.ETY_TestTimes.grid(row=1, column=2)
label15 = Label(FRM_TestTimes, text=" *最多1000000次", font=("宋体", 12), width=23)
label15.grid(row=1, column=3, columnspan=1)
self.CHK_DynamicShowValue = IntVar(value=0)
self.CHK_DynamicShow = Checkbutton(FRM_TestTimes, text="显示动态-影响速度,最多1000次", font=("宋体", 12),
variable=self.CHK_DynamicShowValue)
self.CHK_DynamicShow.grid(row=1, column=4)
FRM_Buttons = Frame(window)
FRM_Buttons.grid(row=4, column=1, padx=20, pady=0)
BT_NoChange = Button(FRM_Buttons, text="每次都不换", width=15, height=3, font=("宋体", 12),
command=self.processNoChange)
BT_NoChange.grid(row=2, column=1)
BT_Change = Button(FRM_Buttons, text="每次都换", width=15, height=3, font=("宋体", 12),
command=self.processChange)
BT_Change.grid(row=2, column=2)
BT_Quit = Button(FRM_Buttons, text="不玩了", width=15, height=3, font=("宋体", 12),
command=self.processQuit)
BT_Quit.grid(row=2, column=3)
BT_SourceCode = Button(FRM_Buttons, text="查看源码/下载", width=15, height=3, font=("宋体", 12),
command=self.processSourceCode)
BT_SourceCode.grid(row=2, column=4)
label16 = Label(FRM_Buttons, text="\n", font=("宋体", 6))
label16.grid(row=3, column=1)

# *********************************** 三门问题介绍 **********************************************
FRM_Intro = Frame(window, bg="white")
FRM_Intro.grid(row=1, rowspan=14, column=2, padx=6, pady=6, sticky=W + N)
label17 = Label(FRM_Intro, text='三 门 问 题', font=("宋体", 28, "bold"), height=2, bg="white")
label17.grid(row=1, column=1, columnspan=2)
PIC_Intro = PhotoImage(file="三门问题试验\\三门问题.gif")
label18 = Label(FRM_Intro, image=PIC_Intro, bg="white")
label18.grid(row=2, column=1, columnspan=2)
message = Message(FRM_Intro, text="\n 三门问题是人工智能公司面试时经常提问的问题。它是关于贝叶斯公式的概率论问题。这个仿真模拟验证有助于理解。\n\n"
" 【三门问题描述】\n"
" 这是个猜奖游戏,游戏规则和过程:\n"
" 1、在台上放3扇门,关着。\n"
" 2、主持人随机地分别在门后面放两只羊和一台车。\n"
" 3、你选一扇门,门后的奖品归你。把你的选择告诉主持人。\n"
" 4、主持人在剩下的两扇门中打开一扇后面是羊的门。\n"
" 5、主持人对你说:我已经帮你排除了一扇有羊的门。现在剩下两扇门了。你现在可以选择是否换掉你原来的选择。\n"
" 6、不论你换不换,你所选择的门后的奖品都归你。\n\n"
" 【问题】为了得到车,你换不换?——试试再说!\n\n", font=("宋体", 11), width=500, bg="white")
message.grid(row=3, column=1, columnspan=2)
PIC_BSV = PhotoImage(file="三门问题试验\\大系统观logo.gif")
label19 = Label(FRM_Intro, image=PIC_BSV, bg="white")
label19.grid(row=4, column=1, sticky=W)
PIC_BSVQRCode = PhotoImage(file="三门问题试验\\微信公众号二维码200.gif")
label20 = Label(FRM_Intro, image=PIC_BSVQRCode, bg="white")
label20.grid(row=4, column=2, sticky=W)
label21 = Label(FRM_Intro, text='大系统观指导人工智能\n\n', font=("宋体", 14, "bold"), fg="green", bg="white")
label21.grid(row=5, column=1, columnspan=2)

# 窗口主循环
window.mainloop()

# ******************************************** 处理按键事件 ******************************************
def processNoChange(self): # 每次都不换
Test_Exe(self, "不换")

def processChange(self):
Test_Exe(self, "换")

def processQuit(self):
os._exit(0)

def processSourceCode(self):
url = "https://www.cnblogs.com/BigSystemsView/p/13901753.html"
webbrowser.open(url)

# ******************************* ThreeDoorsTest类定义结束 ************************************
def InitWinData(win_T, ChangeOrNot): # 初始化数据
# 清空数据
win_T.LB_Strategy["text"] = ""
win_T.TXT_SingleData.delete('1.0', 'end') # 清空单次试验数据
win_T.TXT_Stat.delete('1.0', 'end') # 清空单次试验数据
win_T.LB_TimesFinished["text"] = ""
win_T.LB_Prob["text"] = ""
win_T.LB_TimeLen["text"] = ""

TotalTestTimes = win_T.TotalTestTimes.get()
if win_T.CHK_DynamicShowValue.get() == 0: # 根据是否显示动态,设定最大实验次数
if TotalTestTimes > MAX_TEST_TIMES:
TotalTestTimes = MAX_TEST_TIMES
win_T.TotalTestTimes.set(MAX_TEST_TIMES)
win_T.ETY_TestTimes.update()
else:
if TotalTestTimes > MAX_TEST_TIMES_DYNAMIC:
TotalTestTimes = MAX_TEST_TIMES_DYNAMIC
win_T.TotalTestTimes.set(MAX_TEST_TIMES_DYNAMIC)
win_T.ETY_TestTimes.update()

win_T.LB_Strategy["text"] = ChangeOrNot
if win_T.CHK_DynamicShowValue.get() == 0: # 如果不显示动态,则显示"正在计算…"提示
win_T.FRM_Computing.grid(row=1, rowspan=5, column=1)
win_T.LB_Computing.grid(row=1, column=1, columnspan=10)
win_T.LB_Computing.update()

def Test_Exe(win_T, ChangeOrNot):
# 定义并初始化变量
Door = [0, 0, 0] # 定义3扇门。 Door[i] = 0:羊,1:车,2:被主持人打开(羊)
TotalTestTimes = 0 # 试验总次数
CarNo = SheepNo = YouSelectNo = HostOpenNo = 0 # 车所在门号,羊所在门号,你选择的门号,猜对(得车)次数
TimesCarDoor = [0, 0, 0] # 车在各个门的总次数,不可以用连= ,那意味着不同名称指向同一个列表
TimesYouSelectDoor = [0, 0, 0] # 你选择各个门的总次数
TimesHostOpenDoor = [0, 0, 0] # 主持人打开各个门的总次数
TimesGetCar = TimesGetSheep = 0 # 得到车的总次数,得到羊的总次数
Start = Finish = 0 # 计时
OneRecord = "" # 单次试验数据记录
tmpstr = ""

InitWinData(win_T, ChangeOrNot) # 窗口数据初始化
TotalTestTimes = int(win_T.TotalTestTimes.get())
i_ShowStep = int(TotalTestTimes / 1000)
if i_ShowStep < 1:
i_ShowStep = 1

Time_Start = time.time()
# =============================== 开始多次模拟试验 ===============================
for i in range(1, TotalTestTimes + 1):

# ****************************** 开始一次试验 *****************************
# 初始化三扇门
Door = [0, 0, 0] # 放置三扇门,均放置羊,0代表羊
CarNo = random.randint(0, 2) # 主持人随机地把一扇门后面的羊换成车
Door[CarNo] = 1 # 1代表车
TimesCarDoor[CarNo] += 1 # 统计每个门放置车的总次数
OneRecord = str(i) + "\t" + str(CarNo) + "\t" # 记录每一次的情况

# 你的第一次选择,你随机选一扇门
YouSelectNo = random.randint(0, 2) # 你随机选一扇门
TimesYouSelectDoor[YouSelectNo] += 1 # 统计每个门被选择的总次数
OneRecord += str(YouSelectNo) + "\t" # 记录每一次选择

# 主持人打开一扇有羊的门
for j in range(0, 3):
if j != YouSelectNo and Door[j] == 0: # 主持人打开你没选且有羊的任何一个门,可以取小号的门
HostOpenNo = j
TimesHostOpenDoor[j] += 1
break
OneRecord += str(HostOpenNo) + "\t" # 记录主持人每次打开的门号

# 你换不换
OneRecord += ChangeOrNot + "\t"
if ChangeOrNot == "换":
if YouSelectNo == 0:
if HostOpenNo == 1:
YouSelectNo = 2
else:
YouSelectNo = 1
elif YouSelectNo == 1:
if HostOpenNo == 0:
YouSelectNo = 2
else:
YouSelectNo = 0
elif YouSelectNo == 2:
if HostOpenNo == 0:
YouSelectNo = 1
else:
YouSelectNo = 0

# 揭晓
if Door[YouSelectNo] == 1:
OneRecord += "车*"
TimesGetCar += 1
else:
OneRecord += "羊"
TimesGetSheep += 1

# 是否动态显示数据
if win_T.CHK_DynamicShowValue.get() == 1:
# 显示单次试验数据记录
win_T.TXT_SingleData.insert(END, OneRecord + "\n")
win_T.TXT_SingleData.yview_moveto(1)
win_T.TXT_SingleData.update() # 动态更新
# 显示多次试验的数据统计,没10次刷新一下显示
if i % 10 == 0:
win_T.TXT_Stat.delete('1.0', 'end')
win_T.TXT_Stat.insert(END,
"汽车所在" + "\t" + str(TimesCarDoor[0]) + "\t" + str(TimesCarDoor[1]) + "\t" + str(
TimesCarDoor[2])
+ "\t" + str(TimesCarDoor[0] + TimesCarDoor[1] + TimesCarDoor[2]) + "\n")
win_T.TXT_Stat.insert(END, "第一次选" + "\t" + str(TimesYouSelectDoor[0]) + "\t" + str(
TimesYouSelectDoor[1]) + "\t" + str(TimesYouSelectDoor[2])
+ "\t" + str(
TimesYouSelectDoor[0] + TimesYouSelectDoor[1] + TimesYouSelectDoor[2]) + "\n")
win_T.TXT_Stat.insert(END,
"主持人开" + "\t" + str(TimesHostOpenDoor[0]) + "\t" + str(
TimesHostOpenDoor[1]) + "\t" + str(
TimesHostOpenDoor[2])
+ "\t" + str(
TimesHostOpenDoor[0] + TimesHostOpenDoor[1] + TimesHostOpenDoor[2]) + "\n")
win_T.TXT_Stat.insert(END, "得车次数" + "\t" + "\t" + "\t" + "\t" + str(TimesGetCar) + "\n")
win_T.TXT_Stat.insert(END, "得羊次数" + "\t" + "\t" + "\t" + "\t" + str(TimesGetSheep))

win_T.LB_TimesFinished["text"] = i
win_T.LB_Prob["text"] = (str(TimesGetCar / i * 100))[0:5] + "%"
Time_End = time.time() # 动态显示用时
win_T.LB_TimeLen["text"] = int(Time_End - Time_Start + 0.5)
else:
if i % i_ShowStep == 0:
tmpstr = tmpstr + OneRecord + "\n" # 不动态显示时,间隔保存单次试验记录,不间隔的话数据太大,文本框装不下
# print(i)

# ****************************** 一次试验结束 *****************************
# =============================== 多次模拟试验结束 ===============================

# 不动态显示时,最后显示单次试验记录和最终统计数据
if tmpstr != "":
win_T.TXT_Stat.delete('1.0', 'end')
win_T.TXT_SingleData.insert(END, tmpstr)

win_T.TXT_Stat.delete('1.0', 'end')
win_T.TXT_Stat.insert(END, "汽车所在" + "\t" + str(TimesCarDoor[0]) + "\t" + str(TimesCarDoor[1]) + "\t" + str(
TimesCarDoor[2])
+ "\t" + str(TimesCarDoor[0] + TimesCarDoor[1] + TimesCarDoor[2]) + "\n")
win_T.TXT_Stat.insert(END, "第一次选" + "\t" + str(TimesYouSelectDoor[0]) + "\t" + str(
TimesYouSelectDoor[1]) + "\t" + str(TimesYouSelectDoor[2])
+ "\t" + str(
TimesYouSelectDoor[0] + TimesYouSelectDoor[1] + TimesYouSelectDoor[2]) + "\n")
win_T.TXT_Stat.insert(END,
"主持人开" + "\t" + str(TimesHostOpenDoor[0]) + "\t" + str(TimesHostOpenDoor[1]) + "\t" + str(
TimesHostOpenDoor[2])
+ "\t" + str(TimesHostOpenDoor[0] + TimesHostOpenDoor[1] + TimesHostOpenDoor[2]) + "\n")
win_T.TXT_Stat.insert(END, "得车次数" + "\t" + "\t" + "\t" + "\t" + str(TimesGetCar) + "\n")
win_T.TXT_Stat.insert(END, "得羊次数" + "\t" + "\t" + "\t" + "\t" + str(TimesGetSheep))

win_T.LB_TimesFinished["text"] = i
win_T.LB_Prob["text"] = (str(TimesGetCar / i * 100))[0:5] + "%"
Time_End = time.time() # 动态显示用时
win_T.LB_TimeLen["text"] = int(Time_End - Time_Start + 0.5)

# 隐藏"正在计算…"提示
win_T.LB_Computing.grid_forget()
win_T.FRM_Computing.grid_forget()

# **************************************** 执行主程序 ****************************************
ThreeDoorsTest()

4 VB6和Python3对比及试验结论

(1)纯计算1000000次用时

二者基本相当:VB6用时4秒;Python3用时5秒。

(2)动态显示计算1000次用时

二者相差较大:VB6用时62秒;Python3用时37秒。

Python3用时更少,出乎预料。本以为Python是解释型语言,效率会低一些,而实际情况却相反。原因可能是Python3语言比较新,优化得更好。

(3)程序文件大小

源代码:VB6较长,大约是Python3源代码的2倍。

EXE文件:编译后VB6的可执行文件不到0.5MB,而Python3编译后的可执行文件约为9MB。原因是Python的EXE文件需要打包解释器。

(4)三门问题的模拟试验结论

“不换”的得车概率是1/3,“换”的得车概率是2/3,所以应该选择“换”的策略。

5 文件下载

VB6和Python3源代码和EXE文件均在此下载。

代码以本文附带压缩包为准。

点此下载【三门问题模拟试验程序-VB6和Python3对比】

阿色刚刚尝试用Python编程,专业素养尚浅,不好的习惯还未克服,不足之处敬请批评指正。谢谢!

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章