Как стать автором
Обновить

Парадокс Монти Холла на практике

Верить ли доказательствам этой задачи в интернетах?

Формулировка задачи, как она дана на Википедии:

Представьте, что вы стали участником игры, в которой вам нужно выбрать одну из трёх дверей. За одной из дверей находится автомобиль, за двумя другими дверями — козы. Вы выбираете одну из дверей, например, номер 1, после этого ведущий, который знает, где находится автомобиль, а где — козы, открывает одну из оставшихся дверей, например, номер 3, за которой находится коза. После этого он спрашивает вас, не желаете ли вы изменить свой выбор и выбрать дверь номер 2. Увеличатся ли ваши шансы выиграть автомобиль, если вы примете предложение ведущего и измените свой выбор?


В той же википедии написано, что парадоксом это называется потому что интуиция и математика дают тут разные результаты. Интуиция подсказывает, что от замены выбора ничего не поменяется, но в интернетах есть примеры доказательств, что вероятность выйгрыша после замены выбора будет составлять 2/3.

Так как все эти доказательства мне показались неубедительными, я решил проверить свою интуицию на практике. Моя интуиция, как и интуиция большинства людей, подстказывает мне, что замена выбора никак не должна влиять на вероятность выйгрыша, т. е. после того, как ведущий открывает одну из ячеек, меняете вы выбор или нет — не имеет большой разницы.

Итак, проверим парадокс при помощи программной модели. Для моделирования будем использовать аппаратный генератор случайных чисел и C++. Класс, который моделирует процесс игры:
class Secret {
public:
  // В конструкторе случайным образом выбирается выигрышная коробочка
  Secret(unsigned int random): _choice(-1), _opened(-1) {
    _boxes.push_back(false);
    _boxes.push_back(false);
    _boxes.push_back(false);
    int index = random % 3;
    _boxes[index] = true;
  }

  // В этом методе случаныйм образом выбирается каробочка
  void MakeChoice(unsigned int random) {
    _choice = random % 3;
  }

  // В этом методе ведущий показывает нам одну из коробочек после нашего выбора
  // Коробочка, которую показывает ведущий, не содержит приз
  void OpenOne(unsigned int random) {
    while(random % 3 == _choice || _boxes[random % 3])
      random += 1;
    _opened = random % 3;
  }

  // В этом методе мы меняем свой выбор на оставшуюся коробочку
  void ChangeChoice() {
    std::set<int> numbers;
    numbers.insert(0);
    numbers.insert(1);
    numbers.insert(2);
    numbers.erase(_opened);
    numbers.erase(_choice);
    _choice = *numbers.begin();
  }

  // Проверяем, выйграли мы что-нибудь или нет
  bool IsWiner() {
    return _boxes[_choice];
  }

private:
  std::vector<bool> _boxes; // Коробочки, true означает, что там приз
  int _choice;              // Выбранная нами коробочка
  int _opened;              // Открытая ведущим коробочка
};

Теперь сам процесс игры:
int main() {
  unsigned int total_games = 10000;

  // Генерируем случайную последовательность при помощи аппаратного генератора
  HardkeyRandomGenerator random_generator;
  RawData random_data = random_generator.GenerateRandom(total_games * 3);

  unsigned int no_choice_change_wins = 0; // Количество выйгрышей, если не менять выбор
  unsigned int choice_change_wins = 0;    // Количество выйгрышей, если менять выбор

  for(int i = 0; i < total_games; ++i) {
    Secret secret(random_data.back());      // Создаем коробочки, в одной из которых приз
    random_data.pop_back();                 // Удаляем использованное случайное число
    secret.MakeChoice(random_data.back());  // Выбираем случайную коробочку
    random_data.pop_back();                 // Удаляем использованное случайное число
    if(secret.IsWiner())                    
      no_choice_change_wins += 1;           // Если мы угадали, то добавляем победу к случаю без изменения выбора
    secret.OpenOne(random_data.back());     // Ведущий открывает одну из коробок
    random_data.pop_back();                 // Удаляем использованное случайное число
    secret.ChangeChoice();                  // Меняем наш выбор на оставшуюся коробочку
    if(secret.IsWiner())                    
      choice_change_wins += 1;              // Если мы угадали, то добавляем победу к случаю с изменения выбора
  }

  std::cout << "Total games:\t" << total_games <<std::endl;
  std::cout << "Simple wins:\t" << no_choice_change_wins << ", " << (static_cast<double>(no_choice_change_wins)/total_games)*100 << "%" << std::endl;
  std::cout << "Magic wins:\t"  << choice_change_wins    << ", " << (static_cast<double>(no_choice_change_wins)/total_games)*100 << "%" << std::endl;
  return 0;
}

Обманула ли меня моя интуиция? К счастью — нет:
Total games:    10000
Simple wins:    3729, 37.29%
Magic wins:     6271, 37.29%


Т. е. замена выбора никак не влияет на вероятность выигрыша.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.