始めに アプレット シミュレーションについて プログラムについて
このアプレットは、簡単な進化シミュレーションを行うものです。 遺伝的アルゴリズムの考え方の理解の助けになるでしょう。
このシミュレーションは、おおよそ以下のように動作します。
画面下部(青い部分)は実行中のシミュレーションの状態を表示する部分です。 左に候補の一覧、右に選択された候補の祖先のリストが表示されます。 また、この部分のcontinueボタンを押すことで、シミュレーションを次のサイクルに 進めることができます。
genetic.applet.StringWorldApplet will appear below in a Java enabled browser.
全体の動作概要は冒頭を参照していただき、ここでは動作各部の詳細を説明します。
初期状態では、seedフィールドは空欄になっています。空欄のまま実行を開始すると、プログラムは自動的に適当なランダムな文字列を生成して、それを最初の候補とします。この最初の候補は、ユーザが指定することも可能です。ただし、goal文字列と完全に文字数が一致している必要があります。また、使用可能な文字はいわゆるASCII文字のみです。
各候補は、children #で指定された数だけ、子供を作成します。その際、文字列のうちいずれか1文字が、ASCIIコードで隣にある文字に変化します。文字の範囲は0x20-0x7Fで、0x20と0x7Fとは隣り合っているものとみなされます。文字の位置、およびどちら側の隣になるかはその都度ランダムに決定されます。
再生産が終了すると、候補の数は(children #) + 1倍に増えます。この中から、最適と思われる候補をcandidates #で指定された数だけに絞り込みます。選択の基準は、文字列中の各位置の文字と、goal文字列の同じ位置の文字とが、ASCIIコード表の中でどれだけ離れているか、を積算した数値です。0x20と0x7Fとは隣り合っているものとみなされます。当然、小さいほうが優位になります。
このアプレットは基本的なコアクラスだけを使って作られていますので、JDK1.1ベースの環境ならおそらくどこでも動くものと思います。動作確認はInprise JBuilder付属のJDK1.1.6およびSUNのJDK 1.2.1のappletviewer、InternetExplorer5.0(英語版・日本語版)、jview(5.00.3167)で行いました。テスト中、JBuilderのJDK1.1.6とSUNのJDK1.2.1でのAWTの動作の違いによる修正なども発生した事実から考えると、爛は言い過ぎかもしれません。
このアプレットはアプリケーションとしても動作可能です。ここからJAR形式の アーカイブをダウンロードできます。実行は、
で可能です。・・・と、思います。jviewがjarを扱えるのかどうか知らないのですが、少なくともjarを解凍(jarコマンドで解凍、もしくは拡張子を.jarから.zipに変えて、zip用の解凍ツールで解凍)すれば、
jview -cp . genetic.applet.StringWorldApplet
で実行可能です。
ソースコードが欲しい方は別途ご連絡ください。
このプログラムのクラス群は、大きく2つに分かれています。汎用的なフレームワークとして設計した部分と、この文字列進化を表現するための部分です。
汎用的なフレームワークにするためのクラスには、以下のような抽象クラスやインターフェースが含まれます。できるだけ汎用的に、かつ自然な形に、というのを第一目標に設計しました。フレームワークですので、実際のプログラムではこれらのクラスやインターフェースを継承や実装して使用することになります。また、実際には、具体クラスのバリエーションの作成を簡単にするため、これらの抽象クラスを継承し、一部機能だけを実装した、より具体クラスに近い抽象クラスもいくつか存在しています。
今回のシミュレーションでは、これらのクラスをそれぞれ、StringGenome、StringCreature、StgringMutagen、などに継承させて使用しています。また、WorldDivineの具体クラスはアプレットと連携させて使うために、別スレッドで動作するようにしています。
(*1) WorldはCreatureに、再生産や自殺を指示するだけで、実際にCreatureを作ったり、削除したりはしません。指示されたCreatureの方が、子供を作ってWorldに登録したり、あるいは自分自身をWorldから削除したりします。これには2つの理由があります。
まず、これによって、柔軟性・汎用性が増します。Worldにコントロールされない、Creatureの自発的な再生産や自殺が可能になりますし、逆にWorldからの指示を無視できるCreatureも可能です。
また、このほうがより現実に沿ったモデルである、という判断もあります。たとえば心臓に突き立てられたナイフ、といった外的要因が、本当にその生物を殺すのでしょうか? ナイフという外因を受けた生物が死ぬかどうかは、その生物自身に依存します。普通の人間なら、心臓のナイフは致命的ですが、彼がもし狼男なら結果は変わってくるということです。
(*2) Creatureは、特に内容を特定していない「live」というメソッドを持っており、Worldはサイクル中でそれを起動します。これは、たとえばCreatureに寿命を持たせたい場合の自殺用、再生産のための準備作業、あるいは、進化の頂点に達することだけが目的でなく、進化途上のCreatureたちにも何らかの作業をさせたい場合、などを想定してのものです。
GUIと協調させて動作させるため(AWTのイベントはもともと別スレッドで動作するので)、シミュレーションは別スレッドを生成して実行させています(もっとも、入力待ちの時はwaitさせ、スレッドが走っているときは入力を不可能にしているため、あまり意味がないかも知れませんが・・)。
本来はシミュレーション用スレッドをひとつ生成し、シミュレーションを何度実行してもそれを使いまわせばよさそうなものですが、JDK1.2.xからは、スレッドの停止・再開などの制御はobsoleteと宣言されています。使えないことはありませんが幸せではないので、使うのを止めました。その結果、スレッドを使いまわすのが面倒になりそうなため、新しくシミュレーションを実行するたびにスレッドを生成するようにしてしまいました。
前のスレッドが残ったりしないかと少々不安ですが、少なくともInternetExplorer5.0のJavaコンソールで見る限りでは、きちんと毎回古いスレッドは死んでいるようです。もし問題が発生するようでしたら、ご連絡ください。
99/05/12 KOIKE, T.