モンティ・ホール問題 AfterEffectsでシミュレーション・検証してみた (プログラム配布あり)

モンティ・ホール問題 AfterEffectsでシミュレーション・検証してみた (プログラム配布あり)

「モンティ・ホール問題」ご存知ですか?

たまにSNSや某掲示板などで取り上げられては、人々を惑わすこの問題。

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

あなたの前に扉が3つあります。
その扉の一つだけ、外に繋がっていて、他はハズレ。
最初に一つの扉を選んでください。
そうしたら私が、残り2つの扉のうち、ハズレの扉を教えます。
そして、もう一度だけ、選びなおしても良いです。

このときに、
A 選びなおさない
B 選びなおす
どちらの方が良いのか?

という問題です。

正解は

 「 選びなおす 」 

方が、正解の確率が高い。(文字を選択すると正解が現れます)

ググれば正解はすぐ出てきますが、知らない方は考えてみてください。

 

しかしここで、多くの人が疑問に思うはずです。

「本当にそれが正解なの?」「実際のところどうなの?」

 

ということで今回は、普通のプログラムではなく、Adobeの映像作成ソフトウェア、

”AfterEffects”で、モンティ・ホール問題のシミュレーションをして、検証してみたいと思います。

 

準備~エクスプレッションとは?~

今回使用する、AfterEffectsですが、前述の通り、映像制作のソフトです。

ただこのソフト内に、”エクスプレッション”という、スクリプト(Javascript)を記述して動かせる機能があります。

その機能をうまく使って、AfterEffects上に、この問題の結果を表示してみたいと思います。

そしてその結果から、今回の疑問の答えを導いていきます。

 

”エクスプレッション”とは、AfterEffects上で動くJavascriptなので、例えば、

このように、一般的なループの文(for文)と、エクスプレッション独自の関数を用いて、計算結果を、テキストに表示させたり、数値を代入して移動させたりなんかもできてしまいます。

エクセルにも”マクロ”という機能があり、一定の計算を自動で動かす機能がありますが、それと同じような機能です。

いちいち数値の入力が面倒な動作や、ループや継続的な運動、などを、手間なく実行したりできます。地震で揺れる表現なんかをするときは、Wiggle()なんていう、独自の関数を使うと、自動で揺らしてくれたりします。

 

それでは早速、プログラムを書いていきましょう。

 

実践

レイヤー構成

AfterEffectsには、Photoshopなどと同じく、”レイヤー”という概念があります。このレイヤーを、今回の検証が実現できるような構造に仕上げていきます。

 1.テキストレイヤー:このレイヤーに、メインとなるエクスプレッション(スクリプト)を実装し、今回の結果を表示させます。

 2.テキストレイヤー:結果の文字の賑やかしに使います。

 3.平面レイヤー:背景 兼 数値を制御するためのエフェクトを適用します。

今回は、3つのレイヤーのとてもシンプルな構造になりました。

 

エクスプレッション制御エフェクト

今回は”スライダー”という、数値を制御できるエフェクトを適用しています。

”シード”というのは、ランダムな数を生成する際に、毎回同じ値になってしまうのを防ぐ、「ランダムのパターン」のような数値です。シードを変更してあげると、ランダムのパターンが変わり、別の数値に変わってくれます。

※本当は、テキストレイヤーに制御を適用しても動くのですが、レイヤーの役割を分けるために、平面レイヤーへ適用しました。

 

実際のスクリプト

それでは、テキストレイヤーに、核となるスクリプトを書いていきましょう。

今回は一応、「選びなおす」前提でプログラムを組んでいます。

こちらです(汚いコードお許しください…)↓

var s = 0;
var n = 0;
var out3 = 0;
var out5 = "";
var total = new Array();
var seed = thisComp.layer("暗いグリーン 平面 1").effect("シード")("スライダー");
var ttf = timeToFrames(t = time + thisComp.displayStartTime, fps = 1.0 / thisComp.frameDuration, isDuration = false) + 1;

for(s = 0; s < ttf; s++){
	
	var coll = 0;
	
	for(n = 0; n < 100 ; n++){

		seedRandom(n + s + seed, true);
		var arr = new Array();
		arr[0] = 1;
		for(var i=1; i < Math.floor( thisComp.layer("暗いグリーン 平面 1").effect("スライダー制御")("スライダー") ) ; i++){
			arr[i] = 0;
		}

		for(var ii = arr.length - 1; ii > 0; ii--){
			var r = Math.floor(Math.random() * (ii + 1));
			var tmp = arr[ii];
			arr[ii] = arr[r];
			arr[r] = tmp;
		}

		var inp = Math.floor( random(0, i ) ) + 1;

		var del = 0;
		var val = false;

		while(val === false){
			del = Math.floor( random(0, i ) ) + 1;
			if( del != inp && arr[del - 1] == 0 ){
				val = true;
				break;
			}
		}

		var out1 = "";
		var out2 = "";
		for(var iii = 0;iii < arr.length ; iii++){
			out1 = out1 + " " + arr[iii];
			if( iii != (inp - 1 ) && iii != (del - 1) ){
				out2 = arr[iii];
			}
		}
		if(out2 == 1){
			coll++;
		}
	}

	out3 = out3 + coll;
	out5 = out5 + " " + coll;
}

var out4 = (Math.floor( (out3 / ttf) * 100 ) ) / 100;

text.sourceText = "【計算 " + ttf + " 回】\r" + out1 + "\r選んだ扉 : " + inp + " 番目\r消した扉 : " + del + "番目\r(" + out5 + ")\r正解した数 : " + coll + " /100\r平均正解率 : " + out4 + " %";

Javascriptが分かる方でも、「thisComp」や「sourceText」など、見慣れない関数が出てくるのが分かると思いますが、これが、AfterEffects特有の関数です。

このコードが何をしているかを簡単に説明すると

① 3つの数値が入った配列 arr[ 1, 0, 0 ]を作る
② 配列 arr の中身をシャッフル
③ 3つのうち一つを選んで削除
④ 選んだもの以外をランダムで選び”0”なら削除
⑤ 残った数値を調べて”1”なら正解
⑥ ①~⑤を100回繰り返して正解率を出す
⑦ 更に回数を重ねて平均正解率を導き出す
  →フレームを進めるごとに回数を重ねる

という動きをするコードです。

で、最後の

text.sourceText = "【計算 " + ttf + " 回】\r" + out1 + "\r選んだ扉 : " + inp + " 番目\r消した扉 : " + del + "番目\r(" + out5 + ")\r正解した数 : " + coll + " /100\r平均正解率 : " + out4 + " %";

で、テキストレイヤーの文字を書き換えてるわけです。

 

スクリプトが書けたので、”シード”の値を10回変えて、10回の正解率の平均を出してみましょう。

 

検証

10回(実質1000回の検証)の結果を以下に記します。

1回目2回目3回目4回目5回目
6969696968
6回目7回目8回目9回目10回目
6867686768

平均を出さなくても、結果が見えてきましたね。

正解率の平均は

68.2

でした!

つまり、モンティ・ホール問題の解

「選びなおす」は正しかった、ということが実証されました!!

まあ確率を出せば分かるんですが、やはり事実に基づく結果は、説得力が違うので、今まで正解を疑っていた人も、これで納得していただいたのではないでしょうか?

最後に、最終的な出力の画面をお見せして終わります。

プログラムファイルはこちらです

  

このように、Aftereffectsのエクスプレッションでは、映像制作以外にも、色々なことに使えるので、これからも色々と勉強を兼ねて、遊んでみたいと思います。