surolog

AI・機械学習・データ分析 と 本 など

サイコロシミュレーションでガウス分布と中心極限定理を理解する

前回ガウス分布をシンプルに可視化しましたが、サイコロを例にもう少しガウス分布を試してみます。

surolog.hatenablog.com


ガウス分布は、統計学や自然科学の分野でとても特別な存在です。
Wikipediaには以下のようにあります。

正規分布統計学や自然科学、社会科学の様々な場面で複雑な現象を簡単に表すモデルとして用いられている。たとえば実験における測定の誤差は正規分布に従って分布すると仮定され、不確かさの評価が計算されている。


生物の体長や植物の花弁サイズなど様々なものの数量の分布として正規分布が表れるらしいです。
(教科書にはそう書いてありました。)

で、なぜそんなに表れやすいのかという理由を説明するのが、中心極限定理というものの存在です。中心極限定理について再びWikipediaを参照しますと

独立な同一の分布に従う確率変数の算術平均(確率変数の合計を変数の数で割ったもの)の分布は、もとの確率変数に標準偏差が存在するならば、もとの分布の形状に関係なく、変数の数が多数になったとき、正規分布に収束する。

と言うことなんです。ややこしい。

これをサイコロで例えてみましょう。
「独立な同一の分布に従う確率変数」を「サイコロ」と言い換えます。
あるサイコロの出目は、他のサイコロの出目に影響しないので、「独立」です。
サイコロを超完璧に作れば、サイコロの目の出方は1から6まで同じになり、「同一の分布に従う」と言えます。
ちなみにそんな出方は「一様分布」と言います。

ということで、上記の中心極限定理を超意訳すると

サイコロ1個の目の出方は一様分布だけど、たくさんのサイコロ同時に振ってその目の和(や平均)を出すと、その出方は正規分布に近いよ。

という感じです。(ガチ勢の人見てたらすいません。。)

・・・本当ですかね?

ということで確かめてみます。
processingを使って、簡単にシミュレートしてみましょう。

まずは、1から6までの数字をランダムに発生させて、擬似的なサイコロとします。
「サイコロ1個を振る」のを繰り返して、その出目をカウントしていきます。


コードは後で載せるとして、結果を可視化すると以下のようになります。

f:id:sator926:20160204214541g:plain

まあ当然、どの出目も一様に増えていきますね。まさに一様分布。
では次に、サイコロ2個で試してみます。
「サイコロ2個を同時に振る」のを繰り返して、その合計値をカウントしていきます。

f:id:sator926:20160204215245g:plain

どうですかね。明らかに一様じゃないですね。中央の値(サイコロ2個の場合は7)の出現回数が明らかに増えて、分布が変わってきています。
では、サイコロ10個でいってみましょう。

f:id:sator926:20160204221142g:plain

より分布が滑らかになってきて、例の釣鐘型がなんとなく見えてきます。このあとしばらく放置すると、以下のような分布なりました。

f:id:sator926:20160204221423p:plain

だいぶ正規分布に近づいていますね。もっと数を増やせば、より顕著にこの傾向が表れます。これが、中心極限定理の示すものということになります。

この定理はとても強力です。その強力さは、元の定理文をもう一度見返すと表れています。

独立な同一の分布に従う確率変数の算術平均(確率変数の合計を変数の数で割ったもの)の分布は、もとの確率変数に標準偏差が存在するならば、もとの分布の形状に関係なく、変数の数が多数になったとき、正規分布に収束する。

「もとの分布形状に関係なく」とあります。すごいです。
このサイコロがイカサマサイコロで、1から5の目の出る確率が0.1%ずつ、6の目の出る確率が99.5%だったとしても、十分多い数のイカサマサイコロを振るのを繰り返してその分布を取れば、普通のサイコロと同じ正規分布に行き着くということです。

この強力な性質により、正規分布統計学、数学、物理学などで、重要な役割を担っているのです。

int[] randomCounts;
int dices=10; //サイコロの数を入力
int pt=1+5*dices;
int fl=400; //framelimit

void setup(){
  size(500, 300);
  randomCounts = new int[pt];
  frameRate(50);
  textAlign(CENTER);
}

void draw(){
  background(255);
  textSize(15);
  text("dice="+dices, width/2, 20);
  int index=0;
  for(int a=0 ; a<dices ; a++){
    index += int(random(6));
  }
  println(index);
  randomCounts[index]++;
  stroke(0);
  fill(175);
  int w = width/randomCounts.length;
  for(int x=0 ; x < randomCounts.length ; x++){
    rect(x*w, height-randomCounts[x]-20, w-1, randomCounts[x]);
    textSize(7);
    text(x+dices,x*w+w/2, height-5);
  }
}