O Paradoxo de Monty Hall consiste no fato de que, em se descartando algumas escolhas erradas, as que sobram desconhecidas combinam as probabilidades de estarem certas das descartadas.Por exemplo, em tres portas, atras de uma ha um premio. Voce escolhe uma das tres e o mestre de cerimonias abre uma das outras duas, mostrando que o carro nao esta la. A porta que voce escolheu continua com 13 de chance de ser a correta, mas a outra porta nao escolhida ganha a possibilidade de conter o premio da que foi aberta, passando a ser de 23.Isso acontece porque a porta com o premio e a porta escolhida (independente de ser a do premio ou nao) influenciaram juntas na escolha da porta a ser aberta, mesmo que voce nao saiba qual a porta do premio.Muita gente discorda disso, diz que se sobraram duas portas, as chances passa a ser 12+12 e nao 13+23. Pode-se ficar horas (ou dias) discutindo isso. A melhor forma de se verificar isso e testando!Vamos escrever uma classe que MasterOfCeremonies que oferece tres portas, sendo uma premiada:from random import choiceclass MasterOfCeremonies: __slots__ = '__doors __prize __choice'.split() def __init__(self) -> None: self.__doors = 'ABC' self.__prize = choice(self__doors) self.__choice = None @property def doors(self) -> str: return self.__doors @property def wins(self) -> bool: if not self.__choice: raise TypeError return self.__choice == self.__prize def choose(self, door: str) -> None: if door not in self.__doors: raise ValueError self.__choice = doorNossa classe guarda tres portas (A, B e C), apenas uma premiada. Para testar, podemos jogar mil vezes e vermos em quantas vezes ganhamos.Para facilitar, vamos escolher sempre a porta do meio:if __name__ == '__main__': wins = 0 for _ in range(1000): wc = MasterOfCeremonies() door = wc.doors[1] # sempre a porta do meio wc.choose(door) wins += 1 if wc.wins else 0 print('Wins:', wins)Nao importa quantas vezes voce execute esse programa, vai sempre retornar um valor proximo de 333 vitorias o que faz todo sentido! Sao mil lances, 13 da 333.Agora vamos adicionar o paradoxo: um metodo que abre aleatoriamente uma porta sem premio e outro que permite ao jogador trocar para a porta fechada restante.Dentro da classe MasterOfCeremonies adicione os seguintes metodos: def remove(self) -> None: if not self.__choice or len(self.__doors) == 2: raise TypeError doors = set(self.__doors) others = doors - {self.__choices, self.__prize} other = choice(list(others)) doors -= {other} self.__doors = ''.join(list(doors)) def change(self) -> str: if not (self.__choice and len(self.__doors) == 2): raise TypeError doors = set(self.__doors) doors -= {self.__choice} self.__choice = list(doors)[0] return self.__choiceAgora vamos mudar nosso procedimento principal para trocar de porta apos a abertura (remove) da porta sem premio:if __name__ == '__main__': wins = 0 for _ in range(1000): wc = MasterOfCeremonies() door = wc.doors[1] # sempre a porta do meio mc.choose(door) mc.remove() # descarta uma das portas sem premio door = mc.change() # troca a porta escolhida wins += 1 if wc.wins else 0 print('Wins:', wins)Agora vem o mindfuck: nao importa quantas vezes voce execute esse programa, o numero de vitorias em mil sera sempre por volta de 667, 23 das tentativas!Ou seja, o paradoxo realmente funciona.