In order to provide intuition behind the solution of the Monty Hall problem, Antonio Cangiano says:

If there were a billion doors, you picked one, and then Monty proceeded to open up all the remaining doors but one, we’d have a situation where it would be extremely unlikely that you picked the right door at the beginning, while it would be extremely likely that the remaining door was the one that was concealing the car.

Now, what happens when there are more than three doors. In this post, I will modify the solution of my last post to work for any number of doors. It requires a little change to the program.

import Data.List (delete, (\\) ) import Text.Printf (printf) import Control.Monad import Control.Monad.MC

This time, instead of defining Door by listing all alternatives, I define it as an instance of Bounded class.

data Door = Door Int deriving (Show, Eq) numDoors = 3 instance Bounded Door where minBound = Door 1 maxBound = Door numDoors instance Enum Door where toEnum n = Door n fromEnum (Door n) = n doors = [minBound .. maxBound] :: [Door]

Object are the same as before.

data Object = Car | Goat deriving (Show, Eq) type Universe = [Object]

There is one car; all other doors have goats. This can still be generated using the shuffle function.

nature :: MC Universe nature = shuffle numDoors (Car: repeat Goat)

The contestant open one door at random.

contestant :: MC Door contestant = sample numDoors doors

The options available to the host remain the same as before.

options :: Universe -> Door -> [Door] options universe door = filter (\d -> Goat == open universe d) remainder where remainder = delete door doors

We need a generic function to see what is behind a closed door.

open :: Universe -> Door -> Object open universe (Door n) = universe !! (n-1)

The host opens all but one door. We can do this by replacing the sample function with sampleSubset function.

host :: [Door]-> MC [Door] host o = sampleSubset (numDoors-2) l o where l = length o

The strategies are now whether to stick or switch based on the door that the contestant chose, and the doors that the host opened.

strategyStick :: Door -> [Door] -> Door strategyStick d1 d2 = d1 strategySwitch :: Door -> [Door] -> Door strategySwitch d1 d2 = head (doors \\ (d1:d2))

The reward function is same as before.

reward :: Universe -> Door -> Double reward u d = if Car == open u d then 1.0 else 0.0

And so is the generation of the ranom experiment.

randomVariable :: (Door -> [Door] -> Door) -> MC Double randomVariable strategy = do u <- nature c <- contestant h <- host (options u c) let r = reward u (strategy c h) return r

And we see the result.

main = let n = 100000 seed = 42 stats = repeatMC n (randomVariable strategyStick) `evalMC` mt19937 seed in do putStrLn $ printf "

numDoors : %d" numDoors putStrLn $ printf "Mean : %f" (sampleMean stats) putStrLn $ printf "99%% Confidence interval : (%f, %f)" `uncurry` (sampleCI 0.99 stats)

Now lets see the results. For

numDoors : 3 Mean : 0.3334800000000033 99% Confidence interval : (0.3296397390134338, 0.33732026098657275) numDoors : 30 Mean : 0.03466999999999957 99% Confidence interval : (0.03317983597888913, 0.03616016402111001) numDoors : 300 Mean : 0.0038399999999999784 99% Confidence interval : (0.0033362101507491866, 0.00434378984925077)

Well, I think that you get the idea. I am not going to run this thing for a billion doors.