Kodama's home / tips.

"フルハウス 生命の全容" シミュレーション(ruby スクリプト)

スティーブン, J, グールド の "フルハウス 生命の全容"(早川書房) を読んでちょっと数値実験してみた. 以下の数値実験の意味合いに関しては, この本を参照してもらいたい.

"進化" は "進歩" ではないということ

素朴な考えでは進化を種の進歩/改善のように思いうかもしれない. また, 自然淘汰を通じてしだいに複雑さをまして, 下等なものから高等なものへと改善されてゆくと思うかもしれない.

しかし, ダーウィン的なメカニズムは進歩を保証するものではない. より高度な体制へと導く推進力のようなものは仮定しない. 進化は進歩ではないし, "進化の階梯を登る" ことを意味するのではない.

適者生存の概念と遺伝子の中立な変化をあわせて考えてみよう. 遺伝的な変化は小さなランダムな変化だが, 生き残りや繁殖の結果は適応的にランダムでは無い変化をする. ここからは大局的な意味での進歩とは言えないまでも, 適応度の改善と言い得るような変化の傾向がありうることが予想される. 実際, この効果の計算機シミュレーションである "遺伝的プログラム" では 様々な問題でかなりの "改善" 効果を観察できる.

このような, 遺伝子の構成の局所的な改善が仮にあったとしても, "高等な"生物を生み出すようなグローバルな改善の傾向, 高等な生物を目指すトレンドがあるという証明にはならない. "適応度の改善" と所謂 "高度化", "特殊化" とは直接には一致しない. 実際にも, 体の大きさや特殊化の程度などで一方向的な進化の傾向があるとは言えない. むしろ, 一般的な傾向としては, 祖先種よりも子孫種の方が特殊化の程度などが少ないことが多いという. また, 遺伝子レベルだけではなく, 表現型に関しても, 適応度とは無関係な, 中立な変化も多くみられるという. (つまり,全ての形態の変化が適応的とは言えないということ.)

種進化の傾向と (所謂) "高等" な生物

一見したところ, この主張には矛盾または逆説があるように見える.
  1. 種内の適応度は(生存/繁殖)競争によって "改善される" 傾向がある.
  2. 種の変化については, 祖先種より子孫種の方が "高度化するトレンド" は観察されない. むしろ,多少 "複雑さ"(特殊化など)が劣る傾向がある.
  3. 生物界全体では時間とともに "複雑な" 種が発生し増えるように見える

原因の一部は適応度の改善と人間的な解釈での高度化が必ずしも一致しないという事にあるようだ. ("退化"などを考えてみると良い.)

ここでは, 要因の一部としてのランダムな過程を見る事にする. つまり, 仮に "改良する傾向" が無くても多様性の増大に伴って "複雑" な種も発生するという考えを単純な数値シミュレーションで見てみる.

数値実験の概説

現実にはこのような "複雑さ" を表す単一の尺度は無いが, ここでは 複雑さの程度を 0 又はそれ以上のレベルとして線形に表す事にする. 各レベルに属する種の数を数え上げる. レベル 0 の単一の種から始めて, 1 単位時間毎に, 生物種が 2つの子孫種を残すとする. つまり, 種の多様性は 1回毎に 2倍に増えてゆく. このとき 確率 p(<0.5) で元の種よりも "複雑" な種, 確率 1-p で 元の種よりも "単純" な種(最低でレベル0)に変化するものとする.

左右への移動確率が p で決まる不均等なランダムウォークと考えれば良い. p=0.5 の場合, 複雑さを増す種と複雑さを減らす種の数は等しい. p<0.5 の場合には傾向としては変化すると "単純" な種になってゆくはず. p>0.5 の場合には進化すると複雑さを増す傾向を持つ事になる.

実行例

0.1 のように極端に小さな確率の場合でも これに従ってプログラムを実行してみると 全体としては, 時間が経つに従って "複雑" な種も増えてゆく. 現実には p はもっと大きい(0.5に近い)と思われる.

p=1/10 の例
低い ← レベル → 高い
↓時間
1 
2 
4 
8 
15 1 
30 2 
61 2 1 
116 12 
231 21 4 
455 50 6 1 
912 101 10 1 
1825 198 24 1 
3644 403 42 7 
7285 804 94 8 1 
14561 1628 175 19 1 
29144 3224 358 40 2 
58263 6470 719 76 8 
116521 12944 1431 159 16 1 
233040 25878 2872 319 34 1 
466054 51780 5748 632 66 8 
932104 103556 11490 1268 143 14 1 
1864190 207102 22992 2552 282 32 2 
3728327 414219 46018 5106 569 60 4 1 
7456586 828495 92035 10227 1127 125 13 
14913146 1656979 184111 20435 2269 245 26 5 
p=0.8 の例
低い ← レベル → 高い
↓時間
1 
1 1 
1 2 1 
0 3 4 1 
1 1 6 7 1 
0 4 6 10 10 2 
3 2 10 15 16 15 3 
1 9 11 21 29 28 24 5 
2 8 21 33 47 49 49 40 7 
2 9 28 57 76 91 91 85 63 10 
2 14 37 79 130 156 177 167 142 103 17 
6 13 49 115 202 271 308 351 302 231 171 29 
15 25 72 170 304 440 575 629 637 511 375 295 48 
18 50 107 222 450 738 950 1177 1217 1147 924 636 484 72 
28 67 168 337 629 1101 1702 2026 2337 2328 2063 1644 1051 789 114 
45 110 225 520 991 1680 2600 3682 4127 4536 4382 3762 2937 1703 1287 181 
72 155 383 732 1530 2707 4066 5732 7708 8389 8811 8180 6732 5212 2777 2060 290 
108 243 540 1235 2209 4091 6643 9544 12533 15885 16709 16708 15164 12010 9173 4520 3291 466 
148 393 863 1742 3596 6129 10361 15785 21864 26622 31879 32715 31424 28101 21101 15918 7472 5292 739 

実行例(分布図)

p=0.1, 0.4, 0.5, 0.6, 0.8 の5つの例を挙げる. 見た目にも, p<0.5, p=0.5, p>0.5 の3種の違いがわかると思う.
(乱数の影響もあるので, 数回実行して見やすいものを選んでみた.)

p=0.1(<0.5) の場合: 極端に p を小さくしてみた. 指数型の分布.
p=0.1

p=0.4(<0.5) の場合: "退化" 傾向のある場合. 指数型の分布が見える.
p=0.4

p=0.5 の場合: 中立的な "受動的進化" の状況. 通常のランダムウォーク. この辺が現実に近い.
0.4 の場合と違ってピークが少し丸みを帯びて, 正規分布を半分に切り取ったような形になっている. p=0.5

p=0.6(>0.5) の場合: "進歩" の傾向がある "積極的進化"(誘導型進化). 次第に右に移動する山が見える.
p=0.6

p=0.8(>0.5) の場合: "進歩" の傾向が強い, "積極的進化"(誘導型進化) の意味を観察するため, 極端に p を大きくしてみた. 次第に右に移動する山が見える.
p=0.8

分布

p による 分布の形, 平均, ピークの位置の概略.

p の値分布の概形 平均 ピークの位置
p<0.5指数分布小さいまま小さいまま
p=0.5正規分布の右半分大きくなる(時間の平方根に比例)小さいまま
p>0.5正規分布大きくなる(時間に比例)大きくなる(時間に比例)

pが小さい場合, 指数分布で近似できると考えてみる. 隣接する 3 レベルの比が 1: a : a^2 で平衡しているときには以下が成り立つ.

p + (1 - p) a^2 = a.
(1-p) a^2 - a + p = 0
((1-p)a - p) (a-1) = 0
a= p/(1-p), 1
例えば,
p=1/10 とすると a=1/9.
p=1/3  とすると a=1/2.
p=2/5  とすると a=2/3.
p=0.1 の例では,隣接するレベル間の比率が 1:1/9 程度の(近似的な)指数分布となる.

p>0.5 の場合, 0よりも小さいレベルが無いので分布が歪むが, 近似的に正規分布(又は 2項分布)に近くなる. 分布のひろがり(σ)は時間の平方根に比例して大きくなる.

p が 0.5 のとき, 近似的に正規分布の右半分となる. 分布のひろがり(σ)は時間の平方根に比例して大きくなる.

平均

比率 a(a<1, p<0.5) の等比的な分布(指数分布)とすると, レベルの平均値 AVG は以下のようになる.
A1 = 1 + a + a^2 + a^3 + ... =  1/(1-a)
A2 = 0*1 + 1*a + 2*a^2 + 3*a^3 + ... = a/(1-a)^2
AVG = A2/A1 = a/(1-a)
例えば, 
p=1/10 のとき a=1/9, AVG = (1/9)/(8/9) = 1/8.
p=1/3  のとき a=1/2, AVG = (1/2)/(1/2) = 1.
p=2/5  のとき a=2/3, AVG = (2/3)/(1/3) = 2.
時間が経過しても 平均的なレベルは 1/8 のように低いまま保たれる. 平均的な複雑さの増大, 時代とともに高度になって行くトレンドは存在しない. (ついでに言うと, いつまでたっても最多クラスはレベル0のまま.)

pが大きい場合, 二項分布で近似できると考えると, (又は, 負のレベルがないことによる歪みが小さいと考えると,) 時間ごとに 平均値が p-(1-p) = 2p-1 増大する. p=0.8 の例では 0.6 ずつ増大する. p=0.6 の例では 0.2 ずつ増大する.

p=0.5 の場合, 右半分でのランダムウォークなので,時間が経つに従って分布がひろがって行き, 平均値は次第に(時間の平方根に比例して)大きくなってゆくと考えても良い.

モード(分布のピークの位置)

p が小さい場合, 最多のクラスは 0. p が大きい場合, 最多のクラスは 大きくなって行く. p が(その中間の) 0.5 の場合, 最多のクラスは 0.

Ruby スクリプト

結果を gnuplot でグラフ化 してみた.
#!/usr/local/bin/ruby
# evolution.rb
# 2004-06-15 revised
# 2004-05-28 K.Kodama 
def rev_x(a)
	p=0.5 # probability
	b=Array::new(a.size+1,0)
	a.each_with_index{|v,i|
		(v*2).times{
			if p>rand then i1=i+1; else i1=[i-1,0].max; end;
			b[i1]=b[i1]+1
		}
		#v1=(v*2*p).round; i1=i+1; b[i1]=b[i1]+v1
		#v2=v*2-v1; i1=[i-1,0].max; b[i1]=b[i1]+v2
	}
	while(b[-1]==0) do b=b[0..-2]; end
	return b
end;

gpStr=""; gpSep="";
a=[1] # a=[1,2,1]
a.each{|v| print v.to_s+" "}; print "\n"
16.times{|ite|
	a=rev_x(a)
	a.each{|v| print v.to_s+" "}; print "\n"
	fileName="data"+ite.to_s+".txt"
	File::open(fileName,"w"){|f| a.each{|v| f << (v.to_s+"\n") } }
	gpStr=gpStr+gpSep+'"'+fileName+'" with lines'; gpSep=",";
}

## graph using gnuplot
IO.popen("gnuplot","w"){|gp|
	gp.puts "plot "+gpStr
	# gp.puts 'plot "data.txt" with lines'
	sleep 10; #  show it.
	gp.puts 'set terminal png'
	gp.puts 'set output "evolution.png"'
	gp.puts 'replot'
}

Kodama's home / tips.