HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

Is this an elegant/accurate simulation of the Monty Hall problem?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thisproblemthesimulationaccurateelegantmontyhall

Problem

I'm trying to simulate the Monty Hall problem, to statistically determine if there is any benefit to changing my choice after a door containing a goat is open (testing this guy's Monty Hall theory on YouTube).

So, does this manage to simulate it properly? (Just for the curious: Changing doors doesn't seem to have any benefit, but I want to make sure my simulator isn't flawed). I also want style help, if possible!

#include 
#include 
#include 

#define DOOR_COUNT 3
#define TEST_COUNT 1000000

void InitRandom();
int RunTest(char change);
void RandomizeDoors();
int GetRandom(int floor, int roof);
void OpenGoatDoor (int choice);
int OtherDoor (int choice);
int ChoiceWasCar(int choice);

char doors[DOOR_COUNT+1];

int main (const char argc, const char **argv)
{
    int run[2], yes[2];
    InitRandom();

    run[0] = 0;
    run[1] = 0;
    yes[0] = 0;
    yes[1] = 0;
    // Control Group
    for (int i = 0; i  DOOR_COUNT) {
            doorCur = GetRandom(1, DOOR_COUNT);
        } else {
            doorCur++;
        }
    }
    //printf("Opened Door is %d\n", doorCur);
    doors[doorCur] = -1;
}

int OtherDoor (int choice)
{
    int doorCur = 1;
    while ( doorCur == choice || doors[doorCur] == -1 ) {
        doorCur++;
    }
    return doorCur;
}

int ChoiceWasCar(int choice)
{
    return doors[choice] == 1;
}

Solution

ok, so i ended up changing more than was strictly necessary, sorry. as you probably know, it does pay to change doors, so your code did have a bug. i didn't try to trace it down exactly, but i assume it was related to you having 4 doors instead of 3, which i guess was because you had

for (int i = 0; i <= DOOR_COUNT; i++) {


instead of

for (int i = 0; i < DOOR_COUNT; i++) {


in C you always start from 0 and use

-
it wasn't clear to me what the
doors array contained at first. when i understood i clarified it with an enum.

-
it wasn't clear to me what the two groups were ("control" didn't explain things). when i understood i clarified things with another enum (note the
GROUP_COUNT trick which gives the number of entries in the enum, excluding that).

-
given the
group enum, there's no need to have two print statements - we can use an array of group names. and there's no need for arrays of results, since we have everything in a loop now.

-
i clarified the logic in
OpenGoatDoor - the code initialises the door to choice, which we know is "bad" so that the while is triggered and so we only need a single call to RandomDoor.

-
i tried to clarify the logic in
OtherDoor but i am not sure i helped.

-
i don't think
ChoiceWasCar is necessary - with the enum it's pretty clear what return doors[choice] == car means.

here's the modified code. thanks for posting - i was bored and this was interesting to look at.

#include 
#include 
#include 

#undef DEBUG
#define DOOR_COUNT 3
#define TEST_COUNT 1000000

typedef enum {goat, car, opened} door;
door doors[DOOR_COUNT];

typedef enum {nochange, change, GROUP_COUNT} group;
char *groupName[GROUP_COUNT] = {"No Change", "Change"};

void InitRandom();
int RunTest(group g);
void RandomizeDoors();
int RandomDoor();
void OpenGoatDoor(int choice);
int OtherDoor(int choice);

int main (void)
{
  InitRandom();

  for (group g = 0; g < GROUP_COUNT; ++g) {
    int wins = 0;
    for (int i = 0; i < TEST_COUNT; ++i) {
      wins += RunTest(g);
    }
    printf("%s:\n\t%d tests run\n\t%d win car.\n\n", 
           groupName[g], TEST_COUNT, wins);
  }
  return 0;
}

void InitRandom()
{
  unsigned int iseed = (unsigned int)time(NULL);
  srand (iseed);
}

int RunTest(group g)
{
  RandomizeDoors();
  int choice = RandomDoor();
  #ifdef DEBUG
  printf("Choice is: %d\n", choice);
  #endif
  OpenGoatDoor(choice);
  if (g == change) {
    choice = OtherDoor(choice);
  }
  return doors[choice] == car;
}

void RandomizeDoors()
{
  char carDoor = RandomDoor();
  for (int i = 0; i < DOOR_COUNT; i++) {
    doors[i] = carDoor == i ? car : goat;
    #ifdef DEBUG
    printf("Door %d is %d\n", i, doors[i]);
    #endif
  }
  #ifdef DEBUG
  printf("\n");
  #endif
}

int RandomDoor()
{
  // this is very slightly biased, but we don't care.
  return rand() % DOOR_COUNT;
}

void OpenGoatDoor(int choice)
{
  int randomDoor = choice;
  while ( randomDoor == choice || doors[randomDoor] != goat) {
    randomDoor = RandomDoor();
  }
  #ifdef DEBUG
  printf("Opened Door is %d\n", randomDoor);
  #endif
  doors[randomDoor] = opened;
}

int OtherDoor(int choice)
{
  int otherDoor = choice;
  while ( otherDoor == choice || doors[otherDoor] == opened ) {
    otherDoor = (otherDoor + 1) % DOOR_COUNT;
  }
  return otherDoor;
}


for anyone following along at home, gcc needs
-std=c99`.

output:

: gcc -std=c99 mhall.c; ./a.out
No Change:
        1000000 tests run
        333875 win car.

Change:
        1000000 tests run
        667098 win car.

Code Snippets

for (int i = 0; i <= DOOR_COUNT; i++) {
for (int i = 0; i < DOOR_COUNT; i++) {
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#undef DEBUG
#define DOOR_COUNT 3
#define TEST_COUNT 1000000

typedef enum {goat, car, opened} door;
door doors[DOOR_COUNT];

typedef enum {nochange, change, GROUP_COUNT} group;
char *groupName[GROUP_COUNT] = {"No Change", "Change"};

void InitRandom();
int RunTest(group g);
void RandomizeDoors();
int RandomDoor();
void OpenGoatDoor(int choice);
int OtherDoor(int choice);

int main (void)
{
  InitRandom();

  for (group g = 0; g < GROUP_COUNT; ++g) {
    int wins = 0;
    for (int i = 0; i < TEST_COUNT; ++i) {
      wins += RunTest(g);
    }
    printf("%s:\n\t%d tests run\n\t%d win car.\n\n", 
           groupName[g], TEST_COUNT, wins);
  }
  return 0;
}

void InitRandom()
{
  unsigned int iseed = (unsigned int)time(NULL);
  srand (iseed);
}

int RunTest(group g)
{
  RandomizeDoors();
  int choice = RandomDoor();
  #ifdef DEBUG
  printf("Choice is: %d\n", choice);
  #endif
  OpenGoatDoor(choice);
  if (g == change) {
    choice = OtherDoor(choice);
  }
  return doors[choice] == car;
}

void RandomizeDoors()
{
  char carDoor = RandomDoor();
  for (int i = 0; i < DOOR_COUNT; i++) {
    doors[i] = carDoor == i ? car : goat;
    #ifdef DEBUG
    printf("Door %d is %d\n", i, doors[i]);
    #endif
  }
  #ifdef DEBUG
  printf("\n");
  #endif
}

int RandomDoor()
{
  // this is very slightly biased, but we don't care.
  return rand() % DOOR_COUNT;
}

void OpenGoatDoor(int choice)
{
  int randomDoor = choice;
  while ( randomDoor == choice || doors[randomDoor] != goat) {
    randomDoor = RandomDoor();
  }
  #ifdef DEBUG
  printf("Opened Door is %d\n", randomDoor);
  #endif
  doors[randomDoor] = opened;
}

int OtherDoor(int choice)
{
  int otherDoor = choice;
  while ( otherDoor == choice || doors[otherDoor] == opened ) {
    otherDoor = (otherDoor + 1) % DOOR_COUNT;
  }
  return otherDoor;
}
: gcc -std=c99 mhall.c; ./a.out
No Change:
        1000000 tests run
        333875 win car.

Change:
        1000000 tests run
        667098 win car.

Context

StackExchange Code Review Q#33256, answer score: 3

Revisions (0)

No revisions yet.