Kodumaro :: Paradoxo de Monty Hall, final

Publicado em 26 de Maio, 2016
As sombras da programação.
Erlang

Com o mestre de cerimônias criado no primeiro artigo, e o convidado criado no segundo artigo, podemos rodar o jogo!

O jogo será um novo módulo, mh_game. Podemos começar pelos cabeçalhos obrigatórios:

-module(mh_game).
-export(play/0, play/1).

-define(ROUNDS, 1000).

-spec play() -> integer().
-spec play(Rounds :: integer()) -> integer().

A macro -define/2 define o número de rodadas em mil. A função play/0 roda o número padrão de rodadas, enquanto play/1 roda um número determinado de rodadas.

Vamos implementar play/0 para rodar o número padrão de rodadas:

play() -> play(?ROUNDS).

Nada mais complexo que isso. Agora play/1 deve ser responsável por 1levantar o mestre de cerimônias, 2rodar a quantidade correta de rodadas e 3encerrar o processo mestre de cerimônias.

Como precisamos encerrar o processo do mestre de cerimônias, não podemossimplesmente executar a chamada da rodada, ele precisa ignorar os erros para que mh_mc:stop/1 seja executado em qualquer ocasião:

play(Rounds) ->
    mh_mc:start_link(),
    case catch play(Rounds, 0) of Response -> Response end,
    mh_mc:stop(),
    Response.

Agora precisamos da função play/2, que executará cada rodada.

Sendo uma função recursiva, podemos escrever primeiro a condição de parada,que é quando não há mais rodadas, retornando o acumulador:

play(0, Score) -> Score;

O passo deve executar o seguinte procedimento:

  • Perguntar ao convidado que porta ele deseja escolher (mh_guest:choose/0);
  • Perguntar ao mestre de cerimônias qual outra ficou disponível (mh_mc:suggest_another_door/1);
  • Perguntar ao convidado se ele quer mudar de porta (mh_guest:change/2);
  • Perguntar ao mestre de cerimônias qual o prêmio por trás da portaescolhida;
  • Somar 1 ao acumulador se o convidado ganhou um carro.

Segue então o código que executa este procedimento, acrescentando apenas um throw/1 para levantar um exceção caso ocorra um erro:

play(Rounds, Score) when Rounds > 0 ->
    FirstChoice = mh_guest:choose(),
    {ok, Other} = mh_mc:suggest_another_door(FirstChoice),
    Door = mh_guest:change(FirstChoice, Other),
    case mh_mc:get_prize(Door) of
        {ok, goat} -> play(Rounds - 1, Score);
        {ok, car} -> play(Rounds - 1, Score + 1);
        Other -> throw(Other)
    end.

Compile e rode. Meu resultado foi:

sh$ erlc mh_game.erl
sh$ erl
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V7.3 (abort with ^G)
1> mh_game:play().
325
2> mh_game:play().
320
3> mh_game:play().
348
4> mh_game:play().
336
5gt;

Não importa quantas vezes rode, fica sempre próximo de 333 acertos em mil,333‰, ou seja 33,3%, uma chance em três.

No entanto, se compilarmos com a flag -Dchange, o resultado muda radicalmente:

sh$ erlc -Dchange mh_game.erl[br/]
sh$ erl
Erlang/OTP 18 [erts-7.3] [source] [64-bit] [smp:4:4] [async-threads:10] [hipe] [kernel-poll:false] [dtrace]

Eshell V7.3 (abort with ^G)
1> mh_game:play().
645
2> mh_game:play().
673
3> mh_game:play().
681
3> mh_game:play().
665
4>

Novamente o resultado está sempre por volta do complementar, que são 667acertos em mil, 667‰, 66,7%, duas em três.

Com isso, está provado o Paradoxo de Monty Hall!

Todos os arquivos para baixar