2022.09.28

Javaプログラミング

モンティ・ホール問題とは?

3つのドアA、B、Cの1つに車が入っていて、他の2つのドアにはヤギが入っています。車を入れるドアはランダムです。

ドアが閉まった状態で、司会者のモンティがプレイヤーに車が入っているドアを当てさせます。手順は以下のとおりです。

・プレイヤーが、ドアを1つ選んで司会者(モンティ)に伝えます。

・司会者が、ヤギが入っているドアを1つ開きます。このとき、プレイヤーが選んだドアは開きません。

・司会者が、最初に選んだドアを変更しても良いとプレイヤーに伝えます。

この質問の後、プレイヤーがドアを変更した方が良いか、変更しない方が良いかよいか?

これが、モンティ・ホール問題です。

Javaのソースコード

上記のルールに従って、車が入っているドアが当たる回数、当たらない回数を確認するプログラムを作成しました。

このソースコードでは、最初に選んだドア番号を、必ず変えるようにしています。

MontyHallProblem.java

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
public class MontyHallProblem {
	public static void main( String[] args ) {
		// 当たりの回数
		int atari_num = 0;

		// ハズレの回数
		int hazure_num = 0;

		// 10万回ゲームを実行する
		for ( int i = 1; i <= 100000; i++ ) {
			// 最初に当たりとしたドア番号を乱数で選択
			int first = (int)( Math.random() * 3.0 );

			// 当たりのドア番号atari(0~2)を乱数で発生
			int atari = (int)( Math.random() * 3.0 );
			
			// 開けるドア番号を選択
			int open = 0;
			for ( int j = 0; j < 3; ++ j ) {
				// 最初に選択した番号と、当たり番号は選択しない
				if ( ( j != first ) && ( j != atari ) ) {
					open = j;
					break;
				}
			}

			// 変更するドア番号を取得
			int changed = 0;
			for ( int j = 0; j < 3; ++ j ) {
				// 最初に選択した番号と、開けた番号は選択しない
				if ( ( j != first ) && ( j != open ) ) {
					changed = j;
					break;
				}
			}

			// 変更したドア番号が当たりのドア番号か判定
			if ( changed == atari )
				++ atari_num;
			else
				++ hazure_num;
		}

		// 結果をコンソール出力
		System.out.println( "当たりの回数:" + atari_num );
		System.out.println( "ハズレの回数:" + hazure_num );
	}
}

実行結果

実行

C:\talavax\javasample>java MontyHallProblem

出力結果(実行する毎に結果がかわります)

当たりの回数:67044
ハズレの回数:32956

この結果から、最初に選んだドアを変えた方が当たる確率が約2倍になることがわかります。

Javaソースコードの解説

001
public class MontyHallProblem {

クラス名を、MontyHallProblemとしています。

002
	public static void main( String[] args ) {

このmainメソッドからプログラムを実行します。

003
004
		// 当たりの回数
		int atari_num = 0;

当たり(車を選ぶ)の回数を格納するint型変数atari_numを宣言して0を代入しています。

006
007
		// ハズレの回数
		int hazure_num = 0;

ハズレ(ヤギを選ぶ)の回数を格納するint型変数hazure_numを宣言して0を代入しています。

009
010
		// 10万回ゲームを実行する
		for ( int i = 1; i <= 100000; i++ ) {

for文で、変数iを1~100000まで1ずつ増やしていくループを作成しています。

このループの中にゲームを処理するプログラムを記述します。よって、10万回ゲームを繰り返します。

011
012
			// 最初に当たりとしたドア番号を乱数で選択
			int first = (int)( Math.random() * 3.0 );

ここから、ゲーム開始です。

プレイヤーが選択するドアの番号を乱数で選んで変数firstに代入しています。選ぶドアの番号は0から3です。

014
015
			// 当たりのドア番号atari(0~2)を乱数で発生
			int atari = (int)( Math.random() * 3.0 );

当たりのドア番号を乱数で選んで変数atariに代入しています。選ぶドアの番号は0から3です。

Math.randomメソッド

public static double Math.random()
・乱数を返します。

  パラメータ なし

  戻り値     0.0以上、1.0未満の乱数
017
018
019
020
021
022
023
024
025
			// 開けるドア番号を選択
			int open = 0;
			for ( int j = 0; j < 3; ++ j ) {
				// 最初に選択した番号と、当たり番号は選択しない
				if ( ( j != first ) && ( j != atari ) ) {
					open = j;
					break;
				}
			}

司会者が開けるドアの番号を選んで変数openに代入しています。

for文で、変数jを0~2まで1ずつ増やしていくループを作成しています。

この変数jのうち、プレイヤーが選んだドア番号firstでもなく、当たりのドア番号atariでもない番号を変数openに代入しています。

027
028
029
030
031
032
033
034
035
			// 変更するドア番号を取得
			int changed = 0;
			for ( int j = 0; j < 3; ++ j ) {
				// 最初に選択した番号と、開けた番号は選択しない
				if ( ( j != first ) && ( j != open ) ) {
					changed = j;
					break;
				}
			}

プレイヤーが変更するドアの番号を選んで変数changedに代入しています。

for文で、変数jを0~2まで1ずつ増やしていくループを作成しています。

この変数jのうち、プレイヤーが最初に選んだドア番号firstでもなく、司会者が開けたドア番号openでもない番号を変数changedに代入しています。

037
038
039
040
041
			// 変更したドア番号が当たりのドア番号か判定
			if ( changed == atari )
				++ atari_num;
			else
				++ hazure_num;

プレイヤーが変更したドア番号changedがatariであれば変数atari_numに1を足しています。違えば、変数hazure_numに1を足しています。

044
045
046
		// 結果をコンソール出力
		System.out.println( "当たりの回数:" + atari_num );
		System.out.println( "ハズレの回数:" + hazure_num );

当たりの回数と、ハズレの回数をコンソール出力しています。

次に読んでほしいコンテンツ

処理を繰り返すために使用するfor文について解説しています。

2020.03.23

整数型の変数に1を足すインクリメント、1つ引くデクリメントについて詳しく説明しています。

2020.03.23

乱数の意味と、Math.randomメソッドの使い方をソースコードを使って詳しく解説しています。

2015.12.27

広告