ランダムを使って確率操作

 某スクリプトにて、↓な記述を発見。


trigger = ifelse( (Random % 3)=0, A, ifelse( (Random % 2)=0, B, C ) )
これで、A・B・Cの各事象が1/3の確率でランダムに発生するんだとか。



本当かいな?と思ったので、検証してみる。

 まず、個人的に見づらいのでC風に書き直してみる。

    if( rand() % 3 == 0 )
    {
        A();
    }
    else
    {
        if( rand() % 2 == 0 )
        {
            B();
        }
        else
        {
            C();
        }
    }

こんな感じかね。



それぞれの起こりうる周期を考えてみると、


・A は n % 3 == 0なので、
A--A--A--A--...
・n % 2 == 0ならばB、n % 2 != 0ならばCなので
BCBCBCBCBCBC...
Aの方が先に処理されるため、下の周期に上書きしてみると、


ACBABCACBABC...
となるので、ちゃんと1/3になってるね、うん。



…何か、適当感が否めないので、少し確率っぽく考えると

サンプル数としては、確か、起こりうる確率の最小公倍数だけ用意すれば十分なので、

3 * 2 = 6*1;

この内、n % 3 == 0になるのは、1/3(「0」「3」)

n % 2 == 0になるのは、1/2(「0」「2」「4」)

n % 2 != 0になるのは、1/2(「1」「3」「5」)

ただし、n % 3 == 0が先に処理されるため、

n % 2 == 0 && n % 3 == 0 および n % 2 != 0 && n % 3 == 0の場合は、n % 3 == 0の方が優先される。

よって、

n % 2 == 0になるのは、「2」「4」。

n % 2 != 0になるのは、「1」「5」。

よって、各事象は2/6 = 1/3の確率で起こりうる。

ということですか。





 この方法は、1/3に限らず使えるらしい。

試しに、1/5の場合。


A----A----A----A----A----A----A----A----A----A----A----A----
B---B---B---B---B---B---B---B---B---B---B---B---B---B---B---
C--C--C--C--C--C--C--C--C--C--C--C--C--C--C--C--C--C--C--C--
DEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDEDE

A---BA--B-A-B--AB---A---BA--B-A-B--AB---A---BA--B-A-B--AB---
CEDCDECEDCDECEDCDECEDCDECEDCDECEDCDECEDCDECEDCDECEDCDECEDCDE

AEDCBACEBCAEBEDABECEACDEBADCBEAEBCDABEDCAECEBADEBEACBECABCDE
・A x12
・B x12
・C x12
・D x8
・E x16
…って、アレ?均等じゃないし。

 まぁ、それ以前に、前述の式だと致命的な欠点がある。

前述の式だと毎回rand()しているけど、コレだと毎回異なる数が生成されるので、正確性が落ちる。*2

なので、判定用の乱数を別個に保存しておいた方がいい。

    int rr = rand
    if( rr % 3 == 0 )
    {
        A();
    }
    else
    {
        if( rr % 2 == 0 )
        {
            B();
        }
        else
        {
            C();
        }
    }

こんな感じで。

 まぁこんな方法もありますよという程度に知っておくのもいいかもしれず。




*1:すなわち、0〜5

*2:1/6が1/3 * 1/2になる。