ハト派とタカ派の比率が50対50の時のシミュレーションをC言語で書く
現在、「利己的な遺伝子」を読み進めている。読み進めていく内に、面白いシミュレーションが紹介されていた。それをC言語で実現してみる。その前に、簡単に本書やそのシミュレーションについて述べていこうと思う。
- 作者: リチャード・ドーキンス,日?敏隆,岸由二,羽田節子,垂水雄二
- 出版社/メーカー: 紀伊國屋書店
- 発売日: 2018/02/15
- メディア: 単行本
- この商品を含むブログ (3件) を見る
以前、「攻撃」を読んだ。
- 作者: コンラート・ローレンツ,日高敏隆,久保和彦
- 出版社/メーカー: みすず書房
- 発売日: 1985/05/01
- メディア: 単行本
- 購入: 7人 クリック: 107回
- この商品を含むブログ (19件) を見る
過去に、「攻撃」を参考にして私が書いた記事がある。
nmzfish.hatenablog.com
しかし、「利己的な遺伝子」の中では、「攻撃」の内容に関して、全面的にかつ完全に間違っていると評されている。
「攻撃」の内容を要約すると、「動物は互いの優劣を儀式めいた行為で決め、互いを傷つけない。それは、種の個体数を減らさない工夫であり、そのために動物は進化してきた」というもの。動物は、種の為に利他的に振舞っているとしている。
一方、「利己的な遺伝子」ではこう反論している。「動物は互いの優劣を、互いを傷つけない方法で決める。しかし、それは互いにとって最も利益の高い方法だからである。あくまでも利己的に振舞っているだけ」というものである。
面白い。同じ内容に関して書かれた別著者の本を読み比べたことはある*1。しかし、反対の論理を展開している2冊を読んだのは初めてかもしれない。まだ半分も読み進めていないが、比喩表現が絶妙で、その面でも楽しめている。
By Courtesy: National Human Genome Research Institute - [1] (file), パブリック・ドメイン, Link
「利己的な遺伝子」では、13章ある内、5章では安定性について述べられている。安定性とは、自然界における動物種の数のバランスのことである。例えば、ある草を餌とする草食動物がいるとする。すると、その草食動物を餌とする肉食動物もいるだろう。草食動物が増えれば、餌の草が無くなり、肉食動物は増える。しかし、草が減れば、草食動物が減るので肉食動物も減る。自然界では、これらの数がそれ以上増減しない一定の比率で安定している。ここまでで述べたのは、捕食者と被捕食者の場合である。つまり、別種の動物間の関係である。では同種ではどうか。例えば、AとBとCという同種の動物がいるとする。この3者はオスであり、互いにメスや餌を争うライバルである。ある時、AがBと会ったとする。AがBを殺せばライバルが1人減る。しかし、それはCも同じである。Bを放っておけば、Cと殺しあうかもしれない。何もせずとも利益を得られる。AがBを殺すことは一見利益だが、間接的には他のライバルを助けることになる。平和主義の方が利益にも思えるかもしれない。しかし、戦いで被る不利益よりも、得られる利益の方が大きい場合もある。例えば、巨大なハーレムである。戦いに挑むかどうかを決定するために、これらの利益不利益に対して、損得勘定をすればいい。損得勘定、およびどのように戦うかについてを含めて「戦略」とする。「戦略」とは、例えば「相手を攻撃する。反撃してきたら攻撃をやめて逃げる。」というものである。メイナード・スミスは「戦略」について、「進化的に安定な戦略(ESS:Evolutionarily Stable Strategy)」を定義した。これは、その戦略を個体群のメンバーの大多数が採用すれば、他に取って代わられることのない戦略である。ESSの概念を、攻撃に当てはめるために、最も単純な攻撃パターンについて考察する。それが「ハト派」と「タカ派」である。「ハト派」とは、いわゆる儀式めいた行為によって優劣を決め、互いを傷つけないタイプである。彼らの勝敗は、五分五分であるとする。勝つと利益になるが、負けても不利益はない。しかし、儀式めいた行為には時間がかかる。これは損失になり、両者に発生する。一方、「タカ派」とは、徹底的に戦うタイプである。相手に深い傷を与えるまで戦い続ける。勝敗はこちらも同様に五分五分であるとする。しかし、負ければ負傷という大きな不利益を被る。
この2種類の戦略について、どちらが優れているかをシミュレートする。また、「優れた戦略」を評価するために、得点をつける。得点の高い戦略の方が優れているとする。本記事では、「利己的な遺伝子」において採用された配点を利用する。それは以下のようなものだ。
- 勝てば+50点
- 負ければ0点
- ハト派同士の場合、儀式には時間がかかる。その損失として勝者敗者共に-10点
- タカ派同士の場合、負ければ傷を負うので損失として-100点
早速だが、書いたプログラムを示す。コンパイラはBCC (Borland C++ Compiler)である。なお、こちらのプログラムでは、ハト派とタカ派の比率が50対50の場合の、ハト派とタカ派の1羽辺りの1戦の平均得点を計算する。
#include <stdio.h> #include <stdlib.h> #include <time.h> int GetRandom(int min, int max); int A,B; int bird[100] = {}; int main(void){ int i,pattern; int hato_hato = 0; int hato_taka = 0; int taka_taka = 0; float taka_total,hato_total = 0; for(i = 0; i < 100000; i++){ A = GetRandom(0, 99); B = GetRandom(0, 99); pattern = (A % 2) + (B % 2); switch(pattern){ case 0: if(GetRandom(0, 99) % 2 == 0){ bird[A] -= 100; bird[B] += 50; taka_taka++; } else{ bird[A] += 50; bird[B] -= 100; taka_taka++; } break; case 1: if(A % 2 == 0){ bird[A] += 50; hato_taka++; } else{ bird[B] += 50; hato_taka++; } break; case 2: if(GetRandom(0, 99) % 2 == 0){ bird[A] += 40; bird[B] -= 10; hato_hato++; } else{ bird[A] -= 10; bird[B] += 40; hato_hato++; } break; } } for(i = 0; i <= 99; i += 2){ taka_total += bird[i]; } for(i = 1; i <= 99; i += 2){ hato_total += bird[i]; } printf("average of taka equal %f\n",taka_total/(50 * 1000 * 2)); printf("average of hato equal %f\n",hato_total/(50 * 1000 * 2)); printf("\n"); printf("taka-taka =%d\n",taka_taka); printf("hato-taka =%d\n",hato_taka); printf("hato-hato =%d\n",hato_hato); 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)); }
出力は以下のようになる。タカ派の得点とハト派の得点は、計算によって得られた期待値とほぼ等しい。プログラムの内容を説明していく。ただし、重要だと思われる部分についてのみ説明する。
GetRondom関数は乱数を生成する。中身については以下の記事を参考にした。minとmaxの範囲の乱数を返す。
int GetRondom(int min, int max);
メイン文内のforループである。ループ回数、つまり、行われる総対戦回数は10万回である。bird[0]からbird[99]のそれぞれの鳥が呼び出される回数、つまり、個々の対戦回数はおよそ2000回である。bird[A]で呼び出される場合とbird[B]で呼び出される場合があるため、試行回数を鳥の総数で割った数の2倍となる*2。
AとBに乱数を格納する。これらに格納された番号の鳥を対戦させる*3。bird[偶数]の鳥はタカ派とし、bird[奇数]の鳥はハト派とした。pattern変数は、AとBに格納された変数のmod2の和を格納する。どちらもタカ派であれば、和は0であり、タカ派同士の対戦となる。また、和が1であれば、タカ派vsハト派あるいはハト派vsタカ派の対戦となる。和が2の場合は、ハト派同士の対戦となる。pattern変数の値がそのまま対戦形態となるので、switch文で分岐させ、上記の配点を記述する。タカ派同士とハト派同士では、bird[A]とbird[B]のどちらが勝つかは五分五分であるとした。発生させた乱数のmod2が0か否か、つまり、偶数であるかどうかで判定した。0から99までの範囲で乱数を生成させれば、その数が偶数である確率は50%である。
int main(void){ int i,pattern; int hato_hato = 0; int hato_taka = 0; int taka_taka = 0; float taka_total,hato_total = 0; for(i = 0; i < 100000; i++){ A = GetRandom(0, 99); B = GetRandom(0, 99); pattern = (A % 2) + (B % 2); switch(pattern){ case 0: if(GetRandom(0, 99) % 2 == 0){ bird[A] -= 100; bird[B] += 50; taka_taka++; } else{ bird[A] += 50; bird[B] -= 100; taka_taka++; } break; case 1: if(A % 2 == 0){ bird[A] += 50; hato_taka++; } else{ bird[B] += 50; hato_taka++; } break; case 2: if(GetRandom(0, 99) % 2 == 0){ bird[A] += 40; bird[B] -= 10; hato_hato++; } else{ bird[A] -= 10; bird[B] += 40; hato_hato++; } break; } }
上記に貼り付けた結果より、ハト派の1戦辺りの平均得点は7.5639点であり、タカ派は12.5925点である。計算で得られたハト派の期待値は7.5点であり、タカ派の期待値は12.5点である。得点が高いということは、利益が大きいということであり、個体数が増える。つまり、ハト派とタカ派の安定的な比率は、タカ派の方が少し大きいことがわかる。今後は、ハト派とタカ派のみの集団の場合の安定的な比率をC言語でプログラミングしたシミュレーションで求めてみたい。
- 作者: リチャード・ドーキンス,日?敏隆,岸由二,羽田節子,垂水雄二
- 出版社/メーカー: 紀伊國屋書店
- 発売日: 2018/02/15
- メディア: 単行本
- この商品を含むブログ (3件) を見る
- 作者: コンラート・ローレンツ,日高敏隆,久保和彦
- 出版社/メーカー: みすず書房
- 発売日: 1985/05/01
- メディア: 単行本
- 購入: 7人 クリック: 107回
- この商品を含むブログ (19件) を見る