2009年8月8日土曜日

Erlang 復習

せっかく勉強し始めたErlangを忘れてしまわないうちに復習しておく。 お題は「M個のN面体のサイコロを振って1が出たものの個数を調べるのを1試行とする。これをP試行繰り返して個数と回数の関係を集計し確率を求める。」というのにしてみた。 先日NHKで放送していた米国のサブプライムローン破綻の特集で、このお題を応用で債権リスクを見切って商品化しているというのをやっていたのが、ちょっと面白かった。
-module(t1).
-compile(export_all).

%% N面体のサイコロを振る関数を返す
dice_func(N) -> fun()->trunc(random:uniform() * N) + 1 end.

%% M個のサイコロ D をばら撒いて1が出た個数を数える
scatter(0, _) -> 0;
scatter(M, D) -> inc_when_1(D(), scatter(M-1, D)).

%% X が1のとき Y をインクリメント
inc_when_1(X, Y) ->if X==1 -> Y+1; true->Y end.

%% M個のサイコロDでP回試行。1の個数ごとに回数を集計
exec(P, M, D)->exec(P, P, M, D).
exec(P, 0, _, _)->lists:duplicate(P + 1, 0);
exec(P, I, M, D)->increment(exec(P, I-1, M, D), scatter(M, D)).

%% リスト中の指定位置の整数をインクリメントする
increment([H|T], 0)->[H+1|T];
increment([H|T], P)->[H|increment(T,P-1)].

%% 実行して個数と確率を表示する
main(N, M, P)->
 Repeat = 100,
 Y=[X/(P*Repeat)||X<-sub(N, M, Repeat, P)],
 display(Y).

sub(_, _, 0, P)->lists:duplicate(P + 1, 0);
sub(N, M, I, P)->
 lists:zipwith(fun(X,Y)->X+Y end,
  sub(N, M, I-1, P), 
  exec(P, M, dice_func(N))).

display([])->io:format("end~n");
display([H|T])->io:format("~p~n",[H]), display(T).
3個の6面体サイコロを3回ばら撒く設定でやってみると、以下のような結果が出た。もちろん実行の都度値は変わるがだいたい似たような結果になる。
342> t1:main(6, 3, 3).
0.5466666666666666
0.36666666666666664
0.08
0.006666666666666667
end
筆算によると以下のような値なので、だいたいあってそう。
1が出た個数確率
0個0.5787
1個0.3472
2個0.0694
3個0.0046

0 件のコメント:

コメントを投稿