3.置き換えシステム

イニシエータとジェネレータ

L-Systemはフラクタルと密接な関係があります。フラクタルでは、自分の一部が全体と相似である、という自己相似形とという特徴を持っています。代表的なのがコッホ曲線やシェルピンスキー曲線などです。まずはコッホ曲線の中でも有名な雪片曲線を見てください。

この図形がなぜ自己相似形なのか、またどのように描くのかを解説します。

このような曲線は、イニシエータ(initiator)とジェネレータ(generator)からなります。イニシエータは、基盤のようなもので、ジェネレータは曲線の種のようなものです。上の雪片曲線の場合、次の図の左がイニシエータ、右がジェネレータです。つまり、イニシエータである正三角形の各辺の上にジェネレータを乗せるのです。

イニシエータである正三角形の上に(各辺の上に)ジェネレータを乗せると次のような図形になります。

いま、「乗せる」と書きましたが、「置き換える」の方がよさそうですね。イニシエータの1辺をジェネレータで置き換えました。

さて、イニシエータの1辺をジェネレータで置き換える代わりに、ジェネレータの1辺をジェネレータで置き換えるとどうなるでしょう。もちろん、長さが違いますのでそのままでは置き換えられません。1辺の長さが3分の1ですのでう、ジェネレータを3分の1に縮小します。すると次の図になります。

もとのジェネレータと3分の1の大きさにしたジェネレータは相似形です。そこで、次のように考えます。

ジェネレータ全体とその一部分が相似形である。

これが、自己相似形の考え方です。こうして自己相似形になったジェネレータをイニシエータの上に乗せる(置き換える)と次の図になります。

ジェネレータの大きさをさらに3分の1にして、「置き換え」をしますと、次の図形になります。

最初にあげた図です。この操作を無限に続けていくと、「面積は有限」「辺の長さは無限大」という不思議なことが起こります。それを示すには、高校の数学IIIで学ぶ、無限級数の考え方を用います。(そのため、大学入試問題としても出題されています)

コッホの雪片曲線を描く

では、この雪片曲線をCindyScriptで描いてみましょう。使うのはタートルグラフィクスです。準備として、「3.タートルグラフィクスのコマンドを作る」で作った次の関数を、Initializationスロットに書いておきます。(ここからカットアンドペーストすることもできます)

------------------------------------------------------------------------------------------------------

forward(n):=(

draw([0,0],[n,0]);

translate([n,0]);

);

left(angle):=rotate(angle);

right(angle):=rotate(-angle);

turtle(s,n,angle):=(

    if(s=="A",forward(n));

    if(s=="B",forward(n));

    if(s=="F",forward(n));

    if(s=="+",left(angle));

    if(s=="-",right(angle));

);

tree(s,n,angle):=(

   gsave();

   repeat(length(s),turtle(s_#,n,angle));

   grestore();

);

------------------------------------------------------------------------------------------------------

※ この関数の内容は、「3.タートルグラフィクスのコマンドを作る」の改訂とともに変更されますので注意してください。

tree関数で使っている、「s_#」についてもう一度説明しましょう。アンダーバー _ は、文字列のなかから1文字をとりだす演算子です。#は、repeatで繰り返すときに、Cinderellaが使う「実行変数」というものです。繰り返しの回数が#に入ります。したがって

1回目は sから1番目の文字を取り出す。

2回目は sから2番目の文字を取り出す。

3回目は sから3番目の文字を取り出す。・・・・ となっていきます。すなわち、文字列sの先頭から順に1文字ずつ取りだすことになります。文字列sは、亀に指令する指令文ですので、1つずつ亀に命令を下すことになります。

では、イニシエータとジェネレータを作って、雪片曲線を描きましょう。先にジェネレータを作ります。ここでは、1つの辺の長さを2にしておきましょう。ジェネレータは3つの線分からできています。これを亀に描かせます。次のスクリプトをdrawスロットに書きましょう。

-------------------------------------------------------------------------------------------------------

s="F";      // 最初の状態。線分1つ

kaku=60°;   // 曲がる角度

nagasa=6;   // イニシエータ、ジェネレータの横の長さ 

n=1;        // 自己相似形のレベル

 

generator="F+F--F+F";  // ジェネレータ

 

// 文字列を生成して描く

repeat(n,

    s=replace(s,"F",generator);

);

 

tree(s,nagasa*(1/3)^n,kaku);

-------------------------------------------------------------------------------------------------------

nの値を 2, 3 と変えると、自己相似形のジェネレータができます。

次に、イニシエータを定義して、ジェネレータをイニシエータの上に乗せます。

イニシエータは正三角形ですので、記号で書くと「+F--F--F」です。また、「乗せる」と書きましたが、実際にはFのところをジェネレータで置き換えることになりますので次のようにします。

 

-------------------------------------------------------------------------------------------------------

initiator="+F--F--F";

s=replace(initiator,"F",s);

-------------------------------------------------------------------------------------------------------

 

この2行を、tree(s,nagasa*(1/3)^n,kaku); の前に書きます。

どうでしょう。できましたか?

ところで、今はnを2や3にして、ジェネレーターの自己相似形を作り、最後にイニシエータの上に乗せましたが、イニシエータから出発してジェネレーターでどんどん置き換えていっても同じです。むしろ、このほうが「イニシエータ」の意味に合うでしょう。

できあがりは次のようになります。

-------------------------------------------------------------------------------------------------------

kaku=60°;   // 曲がる角度。

nagasa=6;   // イニシエータ、ジェネレータの横の長さ 

n=1;        // 自己相似形のレベル

 

initiator="+F--F--F";       // イニシエータ

generator="F+F--F+F";  // ジェネレータ

 

 // 文字列を生成して描く

s=initiator;

repeat(n,

    s=replace(s,"F",generator);

);

 

tree(s,nagasa*(1/3)^n,kaku);

-------------------------------------------------------------------------------------------------------

n=0 のときはイニシエータが表示されます。

リンデンマイヤーの本「The Algorithmic Beauty of Plants」 には、次の例が載っています。イニシエータとジェネレータ、角度(kaku)を指定されたものにすれば表示できますのでやってみましょう。長さ(nagasa)と、tree()に渡す2番目の式「nagasa*(1/3)^n」は適当に設定しましょう。

イニシエータ   F-F-F-F

ジェネレータ   F-F+F+FF-F-F+F

角度 90°

さらに、イニシエータは同じで、ジェネレータと回数を変えた次の例も載っています。

ジェネレータを変えるだけでいろいろな図形ができるものです。やってみましょう。

ジェネレータ2つ

Initializationスロットに書いた、亀を動かす関数 turtle を思い出してください。

------------------------------------------------------------------------------------------------------

turtle(s,n,angle):=(

    if(s=="A",forward(n));

    if(s=="B",forward(n));

    if(s=="F",forward(n));

    if(s=="+",left(angle));

    if(s=="-",right(angle));

);

------------------------------------------------------------------------------------------------------

「前へ」の記号として、Fの他にA,Bもあります。ジェネレータを2つ定義するときに、A,Bを使うと便利です。

ただし、2つの文字を同時に書き換える必要があります。CindyScriptでは、リストを使って置き換えをします。リストとは、複数の要素を集めたものだと思ってください。関数の名前は replace で同じですが、同時に置き換えをするものをペアにして [ ] でくくってリストとします。次のスクリプトは、ドラゴン曲線という名前のついたフラクタル図形を描くものです。replaceの使い方をよく見てください。

------------------------------------------------------------------------------------------------------

kaku=90°;   // 曲がる角度

nagasa=2;   // イニシエータ、ジェネレータの横の長さ 

n=10;        // 自己相似形のレベル

 

initiator="+A";     // イニシエータ

generator1="A+B+";  // ジェネレータ1

generator2="-A-B";  // ジェネレータ2

s=initiator;

repeat(n,

  s=replace(s,[["A",generator1],["B",generator2]]);

);

tree(s,nagasa/n,kaku);

------------------------------------------------------------------------------------------------------

このスクリプトで、replaceのリスト部分は、変数に代入することもできます。そこで、ジェネレータは2つまとめてリストで指定することにして、置き換え作業も関数にしてしまいましょう。その関数は Initialization スロットに移します。そうすれば、Drawスロットは簡素化できます。

------------------------------------------------------------------------------------------------------

kaku=90°;   // 曲がる角度

nagasa=2;   // イニシエータ、ジェネレータの横の長さ 

n=10;        // 自己相似形のレベル

 

initiator="+A";     // イニシエータ

generator=[["A","A+B+"],["B","-A-B"]];  // ジェネレータ2

s=makecom(n);

tree(s,nagasa/n,kaku);

------------------------------------------------------------------------------------------------------

---Initialization スロット-------------------------------------------------------------------

// initiator と generator からコマンド列を作成する

makecom(n):=(

  workstr=initiator;

  repeat(n,

    workstr=replace(workstr,generator);

  );

);

------------------------------------------------------------------------------------------------------

結果は次の図の左です。右は「シェルピンスキーのギャスケット」という、これも有名なフラクタル図形です。

イニシエータとジェネレータ、角度と回数を付してあります。長さ(nagasa)と、tree()に渡す2番目の式は適当に調整してください。