# Is this an elegant/accurate simulation of the Monty Hall 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 <stdio.h>
#include <stdlib.h>
#include <time.h>

#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 < TEST_COUNT; i++ ) {
run[0]++;
yes[0] += RunTest(0);
}
for (int i = 0; i < TEST_COUNT; i++ ) {
run[1]++;
yes[1] += RunTest(1);
}
printf("Control Group:\n\t%d tests run\n\t%d yes.\n\n", run[0], yes[0]);
printf("Other Group:\n\t%d tests run\n\t%d yes.\n\n", run[1], yes[1]);
}

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

int RunTest(char change)
{
RandomizeDoors();
int choice = GetRandom(1, DOOR_COUNT);
//printf("Choice is: %d\n", choice);
OpenGoatDoor(choice);
if (change) {
choice = OtherDoor(choice);
}
return ChoiceWasCar(choice);
}

void RandomizeDoors()
{
char n = GetRandom(1,DOOR_COUNT);
for (int i = 0; i <= DOOR_COUNT; i++) {
doors[i] = n == i ? 1 : 0;
//printf("Door %d is %d\n", i, doors[i]);
}
//  printf("\n");
}

int GetRandom(int floor, int roof)
{
int n;
n = (rand()%(roof-floor));
n += floor;
return n;
}

void OpenGoatDoor (int choice)
{
int doorCur = GetRandom(1, DOOR_COUNT);
while ( doors[doorCur] == 1 || doorCur == choice ) {
if (doorCur > 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;
}

• this question might be better suited for programmers.stackexchange.com rather than here. how have you tested this code to make sure that it works? – Malachi Oct 25 '13 at 20:52
• will post an answer this evening (or delete this comment!). would be frustrating if you moved it while i was working on it... – andrew cooke Oct 25 '13 at 21:27
• Your GetRandom() is a tiny bit biased: see Generating a uniform distribution of integers in C. – 200_success Oct 27 '13 at 8:33
• void InitRandom(); should be void InitRandom(void);. void InitRandom(); will accept any arguments. – Banthar Oct 27 '13 at 10:35

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++) {


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


in C you always start from 0 and use < in loops. it's the law.

[in general your code is super neat and pretty good. you had a muddle with array indexing, which is very common, and didn't make use of enum, but apart from that, it was nice code. edit: also, your general approach was fine. this is a good way to test the problem.]

some other issues were:

• the random number generation was strangely general, yet always picked a door. it also started from 1, which i guess is related to the confusion over door array indexing.

• 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 <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;
}


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.

• I got monty.c:22:5: error: first parameter of 'main' (argument count) must be of type 'int', and edited your code to fix it. – 200_success Oct 27 '13 at 8:43