-
Notifications
You must be signed in to change notification settings - Fork 0
/
MontyHall.java
224 lines (187 loc) · 7.79 KB
/
MontyHall.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
package neilcpaul.overthought.gametheory;
import java.text.DecimalFormat;
import java.util.*;
/**
* Created with IntelliJ IDEA.
* User: neilcpaul
* Date: 27/11/14
* Time: 21:17
* Model the Monty Hall Problem, and report the results through this object.
*/
public class MontyHall {
//Capture data
int sw_wins = 0; // Switching wins
int sw_losses = 0; // Switching losses
int ns_wins = 0; // Not switching wins
int ns_losses = 0; //Not switching losses
// Parameters
// Set the number of iterations. Even number pls.
int totalIterationsEach;
int paths;
// Things to play about with
private static int pathsDefault = 3;
private static int iterationsEachDefault = 500000;
// Random generator
Random randomGen = new Random();
public MontyHall()
{
this(iterationsEachDefault);
}
public MontyHall(int iterations)
{
this(iterations, pathsDefault);
}
public MontyHall(int iterations, int outcomes)
{
totalIterationsEach = iterations;
paths = outcomes;
System.out.println("Study of the probability of outcomes in the following scenario:\n"
+ "There is a choice of 3 outcomes, which are hidden.\n"
+ "1 win scenario\n"
+ "2 lose scenarios.\n\n"
+ "One outcome is chosen.\n"
+ "Of the two left, a lose scenario is revealed.\n"
+ "The choice is given to switch to the remaining hidden outcome\n"
+ "After choosing, the outcomes are revealed."
+ "- Is it better to stay or switch?\n\n"
+ "Running simulation...\n"
);
}
public void runModel()
{
runSim();
printResults();
}
private void printResults() {
DecimalFormat df = new DecimalFormat("##.###");
double overallPercentageWins = ((double)(sw_wins+ns_wins)*100)/(totalIterationsEach*2);
double overallPercentageLoses = ((double)(sw_losses+ns_losses)*100)/(totalIterationsEach*2);
double switchingPercentageWins = ((double)(sw_wins)*100)/(totalIterationsEach);
double switchingPercentageLoses = ((double)(sw_losses)*100)/(totalIterationsEach);
double notSwitchingPercentageWins = ((double)(ns_wins)*100)/(totalIterationsEach);
double notSwitchingPercentageLoses = ((double)(ns_losses)*100)/(totalIterationsEach);
double percentageMoreWins = ((double)(ns_wins>sw_wins?ns_wins:sw_wins)*100)/(ns_wins<sw_wins?ns_wins:sw_wins);
System.out.println("Completed simulation. Results:\n");
System.out.println("\nOVERALL");
System.out.println("Number of runs :" + totalIterationsEach*2);
System.out.println("Number of wins :" + (sw_wins+ns_wins));
System.out.println("Number of losses :" + (sw_losses+ns_losses));
System.out.println("Percentage wins :" + df.format(overallPercentageWins));
System.out.println("Percentage losses :" + df.format(overallPercentageLoses));
System.out.println("\nNOT SWITCHING:");
System.out.println("Number of runs :" + totalIterationsEach);
System.out.println("Number of wins :" + ns_wins);
System.out.println("Number of losses :" + ns_losses);
System.out.println("Percentage wins :" + df.format(notSwitchingPercentageWins));
System.out.println("Percentage losses :" + df.format(notSwitchingPercentageLoses));
System.out.println("\nSWITCHING");
System.out.println("Number of runs :" + totalIterationsEach);
System.out.println("Number of wins :" + sw_wins);
System.out.println("Number of losses :" + sw_losses);
System.out.println("Percentage wins :" + df.format(switchingPercentageWins));
System.out.println("Percentage losses :" + df.format(switchingPercentageLoses));
System.out.println("\nSUMMARY:");
System.out.println("Percentage more wins by " + ((ns_wins>sw_wins)?"not switching":"switching") + ": "
+ df.format(percentageMoreWins));
}
private void runSim()
{
// Work out the total number of runs
int totalIterations = totalIterationsEach*2;
// Start playing each round, taking note of the choice and outcome
for (int i = 0; i < totalIterations; i++)
{
// For the first half of rounds: switch, and for the second half: stay.
boolean doSwitch = i<totalIterationsEach;
Outcome outcome = playRound(doSwitch);
// Take note of the result
if (outcome==Outcome.WIN & doSwitch)
{
sw_wins++;
} else if (outcome==Outcome.LOSE && doSwitch)
{
sw_losses++;
} else if (outcome==Outcome.WIN && !doSwitch)
{
ns_wins++;
} else
{
ns_losses++;
}
}
}
public Outcome playRound(boolean doSwitch)
{
// Set up the game
int initialChoice;
int currentChoice;
int winOutcome;
// Explicitly creating the outcome list, and the reveal set
final List<Outcome> outcomes = new ArrayList<>();
final Set<Integer> hidden = new TreeSet<>();
// Pick win outcome. paths = number of outcomes
winOutcome = randomGen.nextInt(paths);
// Populate the path choices
for (int i = 0; i<paths; i++)
{
if (i==winOutcome)
{
outcomes.add(Outcome.WIN);
} else
{
outcomes.add(Outcome.LOSE);
}
hidden.add(i);
}
// Make initial choice. This could be a win outcome or one of two lose outcomes.
initialChoice = randomGen.nextInt(paths);
currentChoice = initialChoice;
// Revealing a lose outcome
// Use 'hidden' to keep track of what is left
// We can't reveal the initial choice, or win outcome
// Since these could be the same thing, we will remove a random LOSE path that isnt the current choice
// In implementation:
// Remove winOutcome and currentChoice from set
// Pull a remaining LOSE randomly
// Add winOutcome and currentChoice back to set for switching
if (winOutcome!=currentChoice)
{
hidden.remove(winOutcome);
}
hidden.remove(currentChoice);
// Quick sanity check that there are actually values to remove
if (hidden.size()>0)
{
// Pick a hidden lose value to remove at random based on hidden set size
int loseChoice = randomGen.nextInt(hidden.size());
// Get the actual value from an indexed version of the set (array)
loseChoice = hidden.toArray(new Integer[hidden.size()])[loseChoice];
// Remove the choice
hidden.remove(loseChoice);
}
// Add the 'known' values back to the set of hidden outcomes for next choice
if (winOutcome!=currentChoice)
{
hidden.add(winOutcome);
}
hidden.add(currentChoice);
// currentChoice is initialChoice at this point
// Make choice or don't make choice
if (doSwitch)
{
// Hidden set should have 2 values in it now
// Remove the initial choice, as it is intended to switch
hidden.remove(currentChoice);
// Grab the last value left, at index 0, put into currentChoice
currentChoice = hidden.toArray(new Integer[hidden.size()])[0];
}
// Check currentChoice for win (from outcomes map)
// Return the outcome
return outcomes.get(currentChoice);
}
private enum Outcome
{
WIN,
LOSE
}
}