sábado, 28 de novembro de 2015

A outra porta

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 três portas, atrás de uma há um prêmio. Você escolhe uma das três e o mestre de cerimônias abre uma das outras duas, mostrando que o carro não está lá. A porta que você escolheu continua com ⅓ de chance de ser a correta, mas a outra porta não escolhida ganha a possibilidade de conter o prêmio da que foi aberta, passando a ser de ⅔.

Isso acontece porque a porta com o prêmio e a porta escolhida (independente de ser a do prêmio ou não) influenciaram juntas na escolha da porta a ser aberta, mesmo que você não saiba qual a porta do prêmio.

Muita gente discorda disso, diz que se sobraram duas portas, as chances passa a ser ½+½ e não ⅓+⅔. Pode-se ficar horas (ou dias) discutindo isso. A melhor forma de se verificar isso é testando!

Vamos escrever uma classe que MasterOfCeremonies que oferece três portas, sendo uma premiada:
from random import choice


class 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 = door

Nossa classe guarda três 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)

Não importa quantas vezes você execute esse programa, vai sempre retornar um valor próximo de 333 vitórias – o que faz todo sentido! São mil lances, ⅓ dá 333.

Agora vamos adicionar o paradoxo: um método que abre aleatoriamente uma porta sem prêmio e outro que permite ao jogador trocar para a porta fechada restante.

Dentro da classe MasterOfCeremonies adicione os seguintes métodos:
    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.__choice

Agora vamos mudar nosso procedimento principal para trocar de porta após a abertura (remove) da porta sem prêmio:
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 prêmio
        door = mc.change()  # troca a porta escolhida
        wins += 1 if wc.wins else 0
    print('Wins:', wins)

Agora vem o mindfuck: não importa quantas vezes você execute esse programa, o número de vitórias em mil será sempre por volta de 667, ⅔ das tentativas!

Ou seja, o paradoxo realmente funciona. Volte lá na Wikipédia, porque a explicação é realmente muito boa. ;-)

[]’s

PS: Eu ia escrever os códigos em Prolog, pois sua abordagem de lidar com domínio de verdades é muito conveniente para este problema, no entanto Python é muito mais didática: já basta a dificuldade em entender o Paradoxo de Monty Hall, não precisamos de mais complicação com uma implementação complexa.