package test1;
import java.util.Random;
// 《三门问题》和《六门问题》的升级版 =>> 《N门问题》java程序
public class MontyHallProblemEnhanced {
// 设置门的数量,即为:《NUM_ALL_DOORS门问题》
public static final int NUM_ALL_DOORS = 6 ;
// 设置总共有NUM_ALL_WINNING_DOORS扇门里有奖, 这个值的设定要满足:0 < NUM_ALL_WINNING_DOORS < NUM_ALL_DOORS 这个条件
public static final int NUM_ALL_WINNING_DOORS = 1 ;
// 设置一开始时,嘉宾可以选择的门的数量,这个值的设定要满足:0 < NUM_ALL_CHOSEN_DOORS < NUM_ALL_DOORS 这个条件
public static final int NUM_ALL_CHOSEN_DOORS = 1 ;
// 设置要主持人可以先开启的门的数量,这个值的设定要满足:0 < NUM_ALL_OPENED_DOORS < NUM_ALL_DOORS - NUM_ALL_WINNING_DOORS - NUM_ALL_CHOSEN_DOORS 这个条件
public static final int NUM_ALL_OPENED_DOORS = 3 ;
// 设置要丢弃已选中的NUM_ALL_CHOSEN_DOORS扇门中的 NUM_ALL_DISCARDED_DOORS 扇门,这个值的设定要满足:0 < NUM_ALL_DISCARDED_DOORS <= NUM_ALL_CHOSEN_DOORS 这个条件
public static final int NUM_ALL_DISCARDED_DOORS = 1 ;
// 设置可以重新选择的门的数量,这个值的设定要满足: 0 < NUM_ALL_REPICKED_DOORS < NUM_ALL_DOORS - NUM_ALL_CHOSEN_DOORS - NUM_ALL_OPENED_DOORS 这个条件
public static final int NUM_ALL_REPICKED_DOORS = 1 ;
// 设置总共要获得 NUM_ALL_WINNING_DOORS 里的 NUM_ALL_GET_WINNING_DOORS 个门才能拿到大奖 ,这个值的设定要满足: 0 < NUM_ALL_GET_WINNING_DOORS <= NUM_ALL_WINNING_DOORS
public static final int NUM_ALL_GET_WINNING_DOORS = 1 ;
// 设置测试的次数,显示 TEST_TIMES次的结果概率。
public static final int TEST_TIMES = 10 ;
// 设置每次测试的结果,都是经过 NUM_ITERATIONS 次循环游戏的结果。
public static final int NUM_ITERATIONS = 1000000;
// 主程序开始运行
public static void main(String[] args) {
// 现在开始计时
long startTime = System.currentTimeMillis();
System.out.println("\t程序的说明:这个程序所代表的是《N门问题》,调节初始的参数,就可以有非常多种的玩法,而且可以算出每一种不同玩法 >> 中大奖和不中大奖的概率。" +
"\n这里测试的是《六门问题》,初始参数的设置,是在这个程序的最上端有说明。游戏是这样的:一开始,嘉宾先选中任意一扇门,接着,"
+ "\n主持人打开其余三扇没有大奖的门,再问嘉宾要不要换门?程序结果输出是:嘉宾选择换门和选择不换门,分别获得大奖的概率。"
+ "\n总共输出TEST_TIMES次结果,每一次的结果都是经过NUM_ITERATIONS次循环模拟游戏得出的大数测试概率\n"
+ " *********************************************************************************************************************************************** \n");
// 现在开始游戏
for(int i = 0 ; i < TEST_TIMES ; i++){ // 进行TEST_TIMES次大数模拟游戏测试,进行TEST_TIMES次的结果输出
int stayDoorsWins = 0; // 嘉宾选择不换门获得大奖的次数
int switchDoorsWins = 0; // 嘉宾选择换门之后获得大奖次数
Random random = new Random();
for (int j = 0; j < NUM_ITERATIONS; j++) { // 每一次输出的结果,都是进行NUM_ITERATIONS次循环游戏后得出的结果
int[] allDoors = new int[NUM_ALL_DOORS]; // 整个allDoors整数数组的初始化默认值全部都为 0
// 第一步:随机生成 NUM_ALL_WINNING_DOORS 个有奖的门的编号
int[] allWinningDoors = new int[NUM_ALL_WINNING_DOORS];
for(int m = 0; m < NUM_ALL_WINNING_DOORS; m++){
do {
allWinningDoors[m] = random.nextInt(NUM_ALL_DOORS);
} while ( allDoors[allWinningDoors[m]] == 1); // 排除掉已经生成的有奖的门的条件
allDoors[allWinningDoors[m]] = 1 ; // 把随机生成的 NUM_ALL_WINNING_DOORS扇有奖的门的编号所对应的allDoors整数数组的默认值 从 0 变为 1 ,为后续的程序的编写和运行做准备
}
// 第二步:嘉宾一开始时随机选到的那NUM_ALL_CHOSEN_DOORS扇门的编号
int[] allChosenDoors = new int[NUM_ALL_CHOSEN_DOORS];
for(int n = 0; n < NUM_ALL_CHOSEN_DOORS; n++){
do {
allChosenDoors[n] = random.nextInt(NUM_ALL_DOORS);
} while ( allDoors[allChosenDoors[n]] == 2 || allDoors[allChosenDoors[n]] == 3); // 排除掉嘉宾已经选好的门
/**接下来,把嘉宾随机选到的那NUM_ALL_CHOSEN_DOORS扇门的编号所对应的整数数组的值改变 >> 有可能选中的是空门,也有可能选中的是有奖的门:
* 如果选到的是空门, 则要把这个门的 编号所对应的整数数组的默认值从0 变为2 ;而如果选到的是有奖的门,而且是没有被嘉宾选过的门,
* 则把这个门的编号所对应的整数数组的值从1变为3 ,这个也是为后续的程序的编写和运行做准备 */
if(allDoors[allChosenDoors[n]] == 0){
allDoors[allChosenDoors[n]] = 2 ;
}else if(allDoors[allChosenDoors[n]] == 1){
allDoors[allChosenDoors[n]] = 3 ;
}
}
// 第三步:主持人随机打开NUM_ALL_OPENED_DOORS扇空门
int[] openedDoors = new int[NUM_ALL_OPENED_DOORS];
for(int p = 0; p < NUM_ALL_OPENED_DOORS ; p++){
do {
openedDoors[p] = random.nextInt(NUM_ALL_DOORS);
} while (allDoors[openedDoors[p]]!= 0 );
/** 排除掉有奖的门,还有嘉宾已经选好的门和主持人已经打开过的门 >> 这个条件也可以改为:
allDoors[openedDoors[p]] == 1 || allDoors[openedDoors[p]] == 2 || allDoors[openedDoors[p]] == 3 || allDoors[openedDoors[p]] == 4 */
allDoors[openedDoors[p]] = 4 ; // 把主持人随机打开的那NUM_ALL_OPENED_DOORS扇空门的编号所对应的allDoors整数数组的默认值从 0 变为 4
}
// 第四步:嘉宾的选择 >> 要不要换门的决策
/**第一种情况:嘉宾选择不换门的策略,即 >> 嘉宾无须丢弃已选中的NUM_ALL_CHOSEN_DOORS扇门中的 NUM_ALL_DISCARDED_DOORS扇门,
* 也无须重新选择NUM_ALL_REPICKED_DOORS扇门,而只要嘉宾选中NUM_ALL_GET_WINNING_DOORS扇有奖的门,就能够获得大奖
* 计算嘉宾选择不换门的策略,能否会中大奖?统计能够中大奖的次数 */
// 设置内部局部计数变量 count
int count = 0 ;
for(int q = 0 ;q < NUM_ALL_DOORS ; q++ ){
if (allDoors[q] == 3 ) {
count ++;
}
}
if( count >= NUM_ALL_GET_WINNING_DOORS){
stayDoorsWins++;
}
count = 0 ; // count 计数清零,为后续的程序运行的计数做准备
/**第二种情况:嘉宾选择换门的策略。那么此时,嘉宾需要先丢弃已选中的NUM_ALL_CHOSEN_DOORS扇门中任意的 NUM_ALL_DISCARDED_DOORS扇门,
然后,再重新选择NUM_ALL_REPICKED_DOORS扇任意的>>没有被嘉宾选过、丢弃过,还有没有被主持人打开过的门。
接下来,嘉宾也是只要选中NUM_ALL_GET_WINNING_DOORS扇有奖的门,就能够获得大奖 */
// 1、先随机丢弃掉已选中的NUM_ALL_CHOSEN_DOORS扇门中的 NUM_ALL_DISCARDED_DOORS扇门
for(int d = 0 ; d < NUM_ALL_DISCARDED_DOORS ; d++ ){
int[] discardedDoors = new int[NUM_ALL_DISCARDED_DOORS];
do {
discardedDoors[d] = random.nextInt(NUM_ALL_DOORS);
} while ( !(allDoors[discardedDoors[d]] == 2 || allDoors[discardedDoors[d]] == 3) ); // 嘉宾随机选择要丢弃掉的他(她)自己已经选择过的NUM_ALL_DISCARDED_DOORS扇门的筛选条件
if(allDoors[discardedDoors[d]] == 2 ){
allDoors[discardedDoors[d]] = 5 ; // 把嘉宾随机丢弃掉的那NUM_ALL_DISCARDED_DOORS扇空门的编号所对应的allDoors整数数组的值从 2 变为 5
}else if (allDoors[discardedDoors[d]] == 3) {
allDoors[discardedDoors[d]] = 6 ; // 把嘉宾随机丢弃掉的那NUM_ALL_DISCARDED_DOORS扇有奖的门的编号所对应的allDoors整数数组的值从 3 变为 6
}
}
// 2、再重新选择NUM_ALL_REPICKED_DOORS扇任意的,而且是,没有被嘉宾选过、丢弃过,还有没有被主持人打开过的门 >> 也就是在allDoors数组数值为 0 或者 1 的门里面来随机选择。
for(int r = 0 ; r < NUM_ALL_REPICKED_DOORS; r++ ){
int[] repickedDoors = new int[NUM_ALL_REPICKED_DOORS];
do {
repickedDoors[r] = random.nextInt(NUM_ALL_DOORS);
} while ( allDoors[repickedDoors[r]] != 0 && allDoors[repickedDoors[r]] != 1 );
/** 这个条件可以改为: allDoors[repickedDoors[r]] == 2
|| allDoors[repickedDoors[r]] == 3 || allDoors[repickedDoors[r]] == 4 || allDoors[repickedDoors[r]] == 5
|| allDoors[repickedDoors[r]] == 6 || allDoors[repickedDoors[r]] == 7 || allDoors[repickedDoors[r]] == 8 */
if(allDoors[repickedDoors[r]] == 0 ){
allDoors[repickedDoors[r]] = 7 ; // 把嘉宾随机重选选中的这扇空门的编号所对应的allDoors整数数组的默认值从 0变为 7
}else if (allDoors[repickedDoors[r]] == 1) {
allDoors[repickedDoors[r]] = 8 ; // 把嘉宾随机重选选中的这扇有奖的门的编号所对应的allDoors整数数组的值从 1变为 8
}
}
// 3、计算嘉宾选择换门的策略后,中大奖的次数
for(int t = 0 ; t < NUM_ALL_DOORS ; t++ ){
if (allDoors[t] == 3 || allDoors[t] == 8) {
count ++;
}
}
if( count >= NUM_ALL_GET_WINNING_DOORS){
switchDoorsWins ++;
}
}
// 输出程序的第(i+1)次结果
System.out.println("第 "+ (i+1) +" 次测试的结果如下: \n");
System.out.println("不换门策略获胜次数: " + stayDoorsWins + "次 。");
System.out.println("换门策略获胜次数: " + switchDoorsWins + "次 。");
System.out.println("不换门策略获胜的概率为: " + stayDoorsWins/(double)NUM_ITERATIONS);
System.out.println("换门策略获胜的概率为: " + switchDoorsWins/(double)NUM_ITERATIONS);
System.out.println("************************************************* ");
}
long endTime = System.currentTimeMillis();
System.out.println("\n >> 整个程序运行用时: " + (endTime - startTime)/1000D + " 秒!");
}
}