« 大半径カーブの動画 | トップページ | トンネル内の駅 »

乱数に関する遊び

自分でも何が楽しくてこんなことをしているのか、サッパリ分からなかったんですが、ある遊びをしてみました。初めから真面目にやってはいませんしこのエントリもあまり真面目に書いてはいないので話半分に読んでくださいな。

まず、こんな問題があります。

箱のなかにカードがn枚あって、1,2,3,...,nと自然数が1つずつ書いてあります。これでゲームをします。
このカードの中から無作為に1枚を取り出しては戻すという操作を繰り返しますが、k回目に出てきたカードが、前回(k-1回目)のカードの数字と同じか小さい数であった場合には、得点をkとしてゲームは終わりです。さて、その得点の期待値(平均)Eと、n→∞のときのEの極限は何でしょうか?

答え
E=(1+1/n)^n → e (e: 自然対数の底~2.71828...)

カードを一枚ずつ取り出していき、数が増えていけばいいのですが、減ったらそこでおしまいです。たとえば、3,8,5と出たら得点は3です。少なくとも2点はもらえますね。

さてその結果が自然対数の底e(Napier数)になるとはなんとも不思議です。ということでこのゲームを実際に行って確かめてみます。

とはいっても十分大きいn枚(例えば100万枚)のカードを用意するのもかったるいので、VRMスクリプトを使ってコンピュータにやらせることにしました。
(なぜココでVRMスクリプトを選んだのか意味不明ですが、あとで無理矢理意味を考えることにしますw)

ですがコンピュータにも出来ることが限られていますので、やりやすいようにすこし話を変えていきます。

箱のなかにn枚のカードがあって、
1/n, 2/n, 3/n, ... , n/n
が1つずつ書かれている

という条件に変えて同じゲームをすることを考えます。もちろん答えは同じです。これでn→∞とすると、箱のなかには0~1の間の相異なる実数が書かれた∞枚のカードがある状態を考えることになります。

ここからVRMスクリプトの話になります。「箱のなかの0~1の相異なる実数が書かれたカード」の代わりに、「rnd命令で得られる0~1の乱数」を使って、同じゲームをVRMビュワーにさせてみます。float型の変数は、本当は有限の少数値(しかも高々数桁ぶん)しか扱えませんが、実験して確かめるだけなのでこんなもんでいいことにします。(floatの精度的には、n=約100万とした感じです。)

そして、チクチク期待値(∞回ゲームを行った時の得点kの平均)を求める代わりに、コンピューターのバカぢからを使って実際に1万ゲーム行って、そのときの得点kの平均を求めさせます。

これで、「確率の取り扱い」「極限の取り扱い」を、それなりな感じでコンピュータに取り扱える範囲の有限の操作に収めることが出来ました。

…なんども言いますがマジメに読むものではありませんからねコレ!

ではスクリプトに落としこんでいきます。

Var TrialCount
Var LeftCount
Var CardCount

Var eidstart
SetEventKey this TrialKicker0 eidstart d

BeginFunc TrialKicker0  // 準備
	set LeftCount 10000  // キーを押した時に実行する規定回数
//	DrawMessage "Start Experiment"
	call this TrialKicker
	SetSignal 1
EndFunc

BeginFunc TrialKicker
	call this Trial  // 実際のゲームを呼び出し
	add TrialCount 1.0
//	DrawVar TrialCount
	sub LeftCount 1
		Var eid
	if> LeftCount 0
		SetEventAfter this TrialKicker eid 10  // 規定回数を実行するループ
	else
		SetEventAfter this Result eid 10  // 規定回数が終わると結果集計
	endif
EndFunc

BeginFunc Trial  // 実際のゲーム1回分のメソッド
	Var BeforeNumber
	setf BeforeNumber 0.0
	Var r
	:forlabel  // ラベル
	rnd r  // rnd命令…カードを引く操作に相当
	add CardCount 1 // 1枚引くごとにカウント
	if> r BeforeNumber
		mov BeforeNumber r
		jump forlabel  // 前回との大小判定をしてラベルジャンプ
	endif
// 1回分のゲームが終わる
EndFunc

BeginFunc Result  // 結果集計
	Var Score
	mov Score CardCount  // 平均を取るため変数をコピー
	cnvfloat Score
	div Score TrialCount  // 平均を取る
	DrawVar Score		// 表示
//	DrawVar TrialCount
	SetSignal 6
EndFunc

こんな感じになりました。(適宜コメントも読んでください)条件分岐とラベルジャンプを使ったループ処理がポイントですかね。

E_chart

チャート図・参照関係はこんな感じになります。

本当は1万ゲームを一気にやらせても良かったんですが、1万回分のラベルジャンプをやらせると、1フレーム内に処理しきれる量を超えるためビュワーにエラーを吐かれてしまいました。やはり1フレームあたりの処理量はそこそこ(せいぜい数百行程度)に抑えなければなりませんね。

1ゲーム=1フレームまで落としてしまったので、キーを押して1万ゲーム分の平均の結果が出るまでかなり時間がかかるようになってしまいました。

まああとはボタンを押して延々とゲームをするんですかねー。とにかく暇なのでお湯を沸かし、コーヒーを入れ、合間にTwitterでもしながら。そして1万回ゲームをするごとにログウィンドウに結果を吐かせます。(1回キーを押すごとに1万回、60フレームなので2分半程度)

Napier1

実行中は信号機が赤になるように設定。(左側)信号機が表示器代わりになるように、諸々のコードはすべて信号機に書いています。

Napier2

意外とブレがありますが、

Napier3

なんと5回目(5万ゲーム分の平均)にして、かなりおしい値が!(また離れてますが)(たまたまこういういい値が出てきても、嬉しいけど実は何の意味もない)

2.71828の前後でウロウロしていることを確かめたところで終わりにします。本当にこんな不思議な数の近くをウロウロするんですね。不思議ですねぇ。というか、ログを吐いて画像でキャプチャするとかいうなんちゅーアホをやってるんですかねぇ。

検証用に使ったレイアウトファイルを置いておきます。乱数のシード値はいじってませんのでこういうとき便利かもしれませんね。

「napier.zip」をダウンロード
ダウンロードファイルおよびその内容物について、一応自己責任でお願いします。

画像だと信号が2本立っていますが片方は不要なので機能を封じてあります。(覗きたい方はどうぞ。)あと車両は485系に置き換えておきました。

これで終わったら本当に遊んでおしまいになってしまうので、この遊び実験の無理矢理考えた意味を記して終わっておきます。

  • 1フレームあたりの処理量はあまり重くしないこと
  • VRMの乱数は、増減の観点では確かにそこそこランダムらしい
んーこのエントリを最後まで読んだ方どれくらいいるのかなー。まあ、枯れ木も山の賑わい。

|

« 大半径カーブの動画 | トップページ | トンネル内の駅 »

VRM:研究」カテゴリの記事

コメント

この記事へのコメントは終了しました。

トラックバック


この記事へのトラックバック一覧です: 乱数に関する遊び:

« 大半径カーブの動画 | トップページ | トンネル内の駅 »