三门问题(Monty Hall problem)亦称为蒙提霍尔问题、蒙特霍问题或蒙提霍尔悖论,大致出自美国的电视游戏节目Let's Make a Deal。问题名字来自该节目的主持人蒙提·霍尔(Monty Hall)。
参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。
当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。
问题是:换另一扇门是否会增加参赛者赢得汽车的机率?如果严格按照上述的条件,那么答案是会。不换门的话,赢得汽车的几率是1/3。换门的话,赢得汽车的几率是2/3。
听着确实是挺反直觉的,但是自己写代码测试确实也是这样的。流言终结者也有一起视频做了证据。
https://www.ixigua.com/7063739968274825735?id=7137582632111604231
解释1:
总共有三种可能的情况,全部都有相等的可能性(1/3):
参赛者挑山羊一号(1/3),主持人挑山羊二号。转换将赢得汽车。
参赛者挑山羊二号(1/3),主持人挑山羊一号。转换将赢得汽车。
参赛者挑汽车(1/3),主持人挑羊一号。转换将失败。
每种情况出现的概率都为1/3,所以切换策略最终赢得汽车的概率为2/3.
还有一种思路,解释2:
第一次选的空门(概率2/3),之后主持人开另一个空门,换门,得到汽车。
第一次选的汽车(概率1/3),之后主持人开一个空门,换门,失败。
import Foundation// 测试总次数
let count = 100000func userPlayGame(strategy: Int) {var result: [(Int,Bool)] = []for i in 0 ..< count {let game = DoorGame()game.userStrategy = strategylet oneGameResult = game.startPlay()let item = (i,oneGameResult)result.append(item)}let winArray = result.filter { item inreturn item.1}let rate = Double(winArray.count) / Double(count)var str = ""if (strategy == 0) {str = "原选择"} else if (strategy == 1) {str = "切换"} else if (strategy == 2){str = "随机切换"}print("用户游玩\(count)次,使用\(str)策略,\(winArray.count)次胜利,胜率:\(rate)")
// print("原始数据",result)
}userPlayGame(strategy: 0)
userPlayGame(strategy: 1)
userPlayGame(strategy: 2)
class DoorGame {// 用户策略// 0: 不改变选择// 1: 改变选择// 2: 50%改策略,50%不改var userStrategy = 0// 只是为了方便解释值的含义enum Strategy: Int {case notChange = 0 // 必定坚持原选择case change = 1 // 必定改变原选择case randomChange = 2 // 50%改变选择, 50%坚持原选择}enum DoorResult: Int {case sheepNotKnow = 0 // 门后是羊case car = 1 // 门后是车case sheepKnow = 2 // 主持人打开的门,必定是羊}func startPlay() -> Bool {// 生成3道门的数据var doorArray = self.buildRandomData()// 用户第一次选择var userSelect = self.userSelect()// 主持人排除一个错误答案, 剩余的结果doorArray = self.removeOneSheep(array: doorArray, userSelect: userSelect)// 根据用户策略决定是否转换if (self.userStrategy == 1) {userSelect = self.userChangeDoor(array: doorArray, userSelect: userSelect)} else if (self.userStrategy == 2) {let random = Int.random(in: 0 ..< 1000)if random % 2 == 0 {userSelect = self.userChangeDoor(array: doorArray, userSelect: userSelect)}}// 计算用户最终是否赢得车let result = self.showResult(array: doorArray, userSelect: userSelect)return result}/// 生成随机数据func buildRandomData() -> [Int] {var array = [0,0,0]let carIndex = Int.random(in: 0...2)array[carIndex] = 1return array}// 用户进行选择func userSelect() -> Int {let userSelect = Int.random(in: 0...2)return userSelect}// 主持人排除一个错误答案func removeOneSheep(array: [Int], userSelect: Int) -> [Int] {var result = arrayfor (i,value) in array.enumerated() {if i == userSelect {continue} else {// 找到第一个是羊的门,变更成已知羊状态if value == 0 {result[i] = 2break}}}return result}// 用户改变自己的选择func userChangeDoor(array: [Int], userSelect: Int) -> Int {var result = -1// [2,1,0] 用户当前选中为1for (i,value) in array.enumerated() {if i == userSelect {continue} else {// 找到第一个是羊的门,移除这条数据if value == 2 {continue} else {result = i}}}assert(result != -1, "出错了")return result}/// 展示结果/// - Parameters:/// - Returns: true: 用户赢得车; false: 用户失败func showResult(array: [Int], userSelect: Int) -> Bool {let value = array[userSelect]if value == 1 {return true}return false}
}