ハト派とタカ派の安定的な比率をC言語で求める

先日、「利己的な遺伝子」を読み終えた。
利己的に振舞う遺伝子が遺伝子プール中に数を増やしていくことが、多くの例から述べられている。他の個体に対して利益を与える行為ですら、自分に返ってくる利益として利己的な行いなのだ。

利己的な遺伝子 40周年記念版

利己的な遺伝子 40周年記念版

また、以前、最も基本的な戦略であるハト派タカ派の比率について記事を書いた。
nmzfish.hatenablog.com
この記事では、ハト派タカ派の比率が50対50の場合の平均得点について述べた。本記事では、両者の平均得点が等しくなる比率、つまり、安定的な比率を求めるプログラムをC言語で書く*1
早速だが、書いたプログラムを以下に示す。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int GetRandom(int min, int max);

int A,B;
int bird[1000] = {};

int main(void){

    int i,j,pattern;
    int hato_hato = 0;
    int hato_taka = 0;
    int taka_taka = 0;
    int taka_all = 500;
    float taka_total,hato_total = 0;
    float taka_average,hato_average = 0;
    
    for(j = 0; j < 100; j++){
  
        for(i = 0; i < 1000000; i++){

            A = GetRandom(0, 999); 
            B = GetRandom(0, 999);

            if(A <= taka_all && B <= taka_all){
                pattern = 0;
            }

            if((A <= taka_all && B > taka_all) || (B <= taka_all && A > taka_all)){
                pattern = 1;
           }

            if(A >= taka_all && B >= taka_all){
                pattern = 2;
            }

            switch(pattern){
                case 0:
                    if(GetRandom(0, 1) == 0){
                        bird[A] -= 100;
                        bird[B] += 50;
                        taka_taka++;    
	       }
                    else{
                        bird[A] += 50;
                        bird[B] -= 100;
                        taka_taka++;
                    }
                break;

                case 1:
                    if(A <= taka_all){
                        bird[A] += 50;
                        hato_taka++;
                    }
                    if(B <= taka_all){
                        bird[B] += 50;
                        hato_taka++;
                    }
                    break;

                case 2:
                    if(GetRandom(0, 1)  == 0){
                        bird[A] += 40;
                        bird[B] -= 10;
                        hato_hato++;
                    }
                    else{
                        bird[A] -= 10;
                        bird[B] += 40;
                        hato_hato++;
                }
                    break;
            }//switch
        }//for(i)

        for(i = 0; i <= taka_all; i++){
            taka_total += bird[i];
            bird[i] = 0;
        }

        for(i = (taka_all + 1); i <= 999; i++){
            hato_total += bird[i];
            bird[i] = 0;
        }

        taka_average = taka_total / (taka_all * 1000 * 2);
        hato_average = hato_total / ((1000 - taka_all) * 1000 * 2);

        taka_total = 0;
        hato_total = 0;

        printf("average of taka equal %f\n",taka_average);
        printf("average of hato equal %f\n",hato_average);
        printf("taka all equal %d\n",taka_all);
        printf("\n");
        printf("taka-taka =%d\n",taka_taka);
        printf("hato-taka =%d\n",hato_taka);
        printf("hato-hato =%d\n",hato_hato);

        if(hato_average < taka_average){
            taka_all++;
        }
        else{
            taka_all--;
        }

    }//for(j)

    printf("taka_all = %d\n",taka_all);

    return 0;
}

int GetRandom(int max, int min){

    static int flag;

    if(flag == 0){
        srand((unsigned int)time(NULL));
        flag = 1;
    }

    return min + (int)(rand() * (max - min + 1.0) / (1.0 + RAND_MAX));
}

前回の記事からの大きな変更点のみ説明する。

ハト派タカ派の比率をより正確に求めるため、鳥の数は1000羽とした。

int bird[1000] = {};

前回の記事では乱数を生成し、偶数か奇数かでタカ派ハト派を決定していた。今回は、タカ派の数を決めておき、その数字以下の番号の鳥をタカ派とした。

int taka_all = 500;

対戦形態の判別のための条件式は、以下のようにした。AとBに0~999までの乱数を格納し、それぞれの番号をtaka_allの数と比較することにより、ハト派タカ派が判別できる。

            A = GetRandom(0, 999); 
            B = GetRandom(0, 999);

            if(A <= taka_all && B <= taka_all){
                pattern = 0;
            }

            if((A <= taka_all && B > taka_all) || (B <= taka_all && A > taka_all)){
                pattern = 1;
           }

            if(A >= taka_all && B >= taka_all){
                pattern = 2;
            }

forループの最後で、ハト派タカ派の1戦辺りの平均得点を計算する。その結果より、タカ派の方が平均得点が高ければタカ派の数が増える。

       if(hato_average < taka_average){
            taka_all++;
        }
        else{
            taka_all--;
        }

jを条件としたforループはタカ派の数を変化させるループである。また、iを条件としたforループは対戦回数である。ループ回数は1000000回であり、鳥が1000羽いることから、それぞれの鳥は2000回対戦を行う。

    for(j = 0; j < 100; j++){
  
        for(i = 0; i < 1000000; i++){

出力は以下のようになった。ハト派タカ派のそれぞれの1戦辺りの平均得点は約6.20~6.30である*2タカ派の数が583であることから、タカ派の全体に対する比率は約12分の7である。これは著者が計算した結果と等しい。

f:id:nmzfish:20190826174418j:plain
出力

私たちが用いている任意の得点システムから計算してみると、安定した比率は、ハト派が一二分の五、タカ派が一二分の七となることがわかる。

*1:簡単な数学の知識を用いれば紙とペンだけで求めることもできる

*2:期待値