Webページでは数式を表すためにLaTeX表記が使えるMathJaxを利用しています。 WebブラウザにはSafari/Chrome/Firefoxを使って下さい(IEでは表示できないようです。)

亀を再帰的に動かす

Pythonの亀グラフィックスのメソッドで確かめたように、亀グラフィックスでは

という単純な行動様式に従って地面を這って、その軌跡を描く。

それでも、亀は驚くべき軌跡を描き出すことができる。

デフォルトでは、亀は原点で右側($x$-軸の正の方向)を向いて、ペンダウン pendown(それ以降は亀が移動した軌跡を描く)ダウンの状態で生まれる。 ただし、亀がペンアップ penupしている間は、ふたたびペンダウンするまで亀が移動した軌跡は描かない。

コッホ Koch曲線

亀 kame が距離 $\ell=$ length を深さ n $(\geqq 0$ だけ turtle_koch(kame, length, n) することを左図のように定義する。

関数 turtle_koch(kame, length, n):

この定義に忠実にPythonで定義したのが、次のスクリプト turtle_koch.py の関数 turtle_koch(kame, length, n) である。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import turtle

def turtle_koch(kame, length, n):
    if n == 0:
        kame.forward(length)
    else:
        turtle_koch(kame, length / 3, n - 1)
        kame.left(60)
        turtle_koch(kame, length / 3, n - 1)
        kame.right(120)
        turtle_koch(kame, length / 3, n - 1)
        kame.left(60)
        turtle_koch(kame, length / 3, n - 1)

mywin = turtle.Screen()
mykame = turtle.Turtle()

mykame.speed(10) # set maximum speed
mykame.penup() # pen up before movin kame to new position (-300, -200)
mykame.goto(-300, -200)
mykame.pendown() # then pen down. draw trace from now

#k = int(raw_input("k-th curve: k = "))
k= 3 # set depth of interation 
turtle_koch(mykame, 500, k) # try other k

mywin.exitonclick() # keep drawing window until mouse click
演習: 上のスクリプト turtle_koch.py を実行しなさい。
演習 亀三匹 kame1, kame2, kame 3 を使ってKoch曲線をつないで右図(深さ 3。クリックで拡大)のように描くスクリプトを書いて実行しなさい(併せてスクリーンショットも)。
(ヒント); 亀をスクリーンのどこに設置し、どの位の大きさでKoch曲線を描くかはいろいろ試してみる。 2匹目の亀 kame2 は次のように生成して這わせる(亀のメソッドはを参照)。 一匹目の亀 kame1の頭の最終向き kame1.heading() や最終位置 kame1.position() を使って kame2 を設定することがポイントだ。 色文字列 colorstring には 'black'(デフォルト)以外に、blue', 'red', 'green', 'white', 'yellow', 'violet', 'orange', 'pink' が使える。
kame2 = turtle.Turtle()
kame2.speed(10)
kame2.color('red')
kame2.setheading(kame1.heading())
kame2.right(120)
kame2.penup()
kame2.setposition(kame1.position())
kame2.pendown()
turtle_koch(kame2, 500, k)

レビィ Levy曲線

亀 kame が距離 $\ell=$ length を深さ n $(\geqq 0$ だけ turtle_levy(kame, length, n) することを左図(クリックで拡大)のように定義する。

関数 turtle_levy(kame, length, n):

この定義に忠実にPythonで定義したのが、次のスクリプト turtle_levy.py の関数 levy_curve(kame, length, n) である。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import turtle
import math

def levy_curve(kame, length, depth):
    if depth == 0:
        kame.forward(length)
    else:
        kame.left(45)
        levy_curve(kame, length/math.sqrt(2), depth - 1)
        kame.right(90)
        levy_curve(kame, length/math.sqrt(2), depth - 1)
        kame.left(45)


mywin =  turtle.Screen()
mykame = turtle.Turtle()
mykame.speed(10)
levy_curve(mykame, 300, 4)

mywin.exitonclick()
演習: 上のスクリプト turtle_levy.py を実行しなさい。 再帰の深さが低いときはたいしたことが起こらないように思える。 しかし、 dept = 4 を越えて曲線がぶつかり始めたときから事情がかわる。 dept = 9 当たりから当初の予想を裏切り始め、時間がかかる驚くべき姿に収束することが示唆される。

Sierpiński gasket シェルピンスキー・ガスケット

三角形(正三角形が原義である)をなす3点 $p_0$, $p_1$, $p_2$ における深さ $n$ の Sierpiński ガスケット $\mathrm{S}(p_0,p_1,p_2,n)$ は、左図(クリックで拡大)のように、3つの深さ $n-1$ のSierpińskiガスケット $\mathrm{S}(p_0,p_a,p_c,n-1)$, $\mathrm{S}(p_a,p_1,p_b,n-1)$ および $\mathrm{S}(p_c,p_b,p_2,n-1)$ からなると定義する。

ここで、点 $p_a$ は $p_0$ と $p_1$ の中点、点 $p_b$ は $p_1$ と $p_2$ の中点、点 $p_c$ は $p_0$ と $p_2$ の中点である。 具体的には、たとえば $p_a=(p_{ax},p_{ay})$ は \[ p_a=(p_{ax},p_{ay})=\frac{p_0+p_1}{2}=\left(\frac{p_{0x}+p_{1x}}{2}, \frac{p_{0y}+p_{1y}}{2}\right) \] である。

すなわち Sierpiński ガスケット $\mathrm{S}(p_0,p_1,p_2,n)$ は次の関係を満たす。 \[ \mathrm{S}(p_0,p_1,p_2,n) = \mathrm{S}(p_0,p_a,p_c,n-1) \cup \mathrm{S}(p_a,p_1,p_b,n-1)\cup \mathrm{S}(p_c,p_b,p_2,n-1) \] であって、真ん中の三角形 $\triangle p_a p_bp_c$ 領域は抜け落ちていることに注意する。

亀でSierpińskiガスケットを描かせる

以下に、与えられた3点からなるリスト point を使って、亀 kame が右図(クリックで拡大)のような Sierpińskiガスケット(深さ 4)を描く関数 turtle_sierpinski(kame, points, depth) を考えよう。 幾何学的には、上の図を描けばよいので、手で描くには簡単だ。 深さによって描く三角形に色を付けるところに亀の工夫がある。

亀 kame に3角形を描かせる

そのために、まず、与えられた3点からなるリスト point を使って亀 kame が3角形を描く関数 draw_triangle(kame, points, color) を定義する。 draw_triangle(kame, points, color) は、亀 kame が3点からなるリスト point を使って 色 color で塗りつぶした三角形を描く関数である。 リスト point は [point[0], point[1], point[2]] としており、さらに各点 point[i] は $x$, $y$-成分を持つリスト [point[i][0], point[i][1]] からなっている。

def draw_triangle(kame, points, color):
    # draw fill-color triangle on clockwise points
    kame.fillcolor(color) # set fill-color
    kame.up() # penup during moving
    kame.goto(points[0][0],points[0][1])
    kame.down() # pendown after positionig
    kame.begin_fill() # set fill-mode
    kame.goto(points[1][0], points[1][1])  # strat point of polygon
    kame.goto(points[2][0], points[2][1])
    kame.goto(points[0][0], points[0][1])  # final point of polygon(3 points in thi scase)
    kame.end_fill() # paint with fill color

ここで、draw_triangle に渡している3角形を描くための3点からリスト points は、上のスクリプトからわかるように、二重のリストになっていて次の構造になっている(実際のスクリプトが上手く動くためには、こうしたデータの整合性について細心の配慮が必要だ)。

points = [points[0], points[1], points[2]]
       = [ [points[0][0], points[0][1]], [points[1][0], points[1][1]], [points[2][0], points[2][1]] ]

点の中点を求める

2点 p1, p2 を与えてその中点の座標を返す関数 get_midpoint(p1, p2) は次のようになる。 ここでも同様に、渡される p1 と p2 については平面上の点として扱えるように構成されていなければならない。

def get_midpoint(p1, p2):
    return ((p1[0] + p2[0]) / 2, (p1[1] + p2[1]) / 2)

Sierpińskiガスケットを描く

以上の準備、与えられた3点からなるリスト point を使って亀 kame が3角形を描くための関数 draw_triangle(kame, points, color) 、、与えられた1二点 p1 と p2 の中点の座標を返す関数 get_midpoint(p1, p2) を使うと、亀 kame 深さ n のSierpińskiガスケットを描く関数 turtle_sierpinski(kame, points, depth) を次のように描くことができる。

深さ n に応じて三角形を描くための色を7成分からなる文字列リスト bgcolor = ['blue', 'red', 'green', 'white', 'yellow', 'violet', 'orange'] から選び出すために、正数商 n % n を使っている。

def turtle_sierpinski(kame, points, depth):
    bgcolor = ['blue', 'red', 'green', 'white', 'yellow',
            'violet', 'orange']
    draw_triangle(kame, points, bgcolor[depth % 7])
    if depth > 0:
        turtle_sierpinski(kame, [points[0], get_midpoint(points[0], points[1]),
                  get_midpoint(points[0], points[2])], depth - 1)
        turtle_sierpinski(kame, [points[1], get_midpoint(points[0], points[1]),
                  get_midpoint(points[1], points[2])], depth - 1)
        turtle_sierpinski(kame, [points[2], get_midpoint(points[2], points[1]),
                  get_midpoint(points[0], points[2])], depth - 1)
演習: 以上の関数定義を含んた、以下を実行するスクリプト turtle_sierpinski_gasket.py を描いて実行しなさい(併せてそのスクリーンショットも撮る)。
mywin =  turtle.Screen()

mykame = turtle.Turtle()
mykame.speed(10)

mypoints = [[-200, -100], [0, 246], [200, -100]] # この3点の座標は概ね正三角形に近い
turtle_sierpinski(mykame, mypoints, 4)

mywin.exitonclick()

ドラゴン dragon曲線


Levy曲線は直線を、それを底辺とする直角二等辺三角形の直交する2辺で置き換えていくものであった。 その置き換えは、直線の進行方向に対して二辺を常に外側に置き換えている。

左図(クリックして拡大)のように、「二等辺三角形の2辺に関して交互に」進行方向について外側と内側に置き換えを繰り返して定義してみるとどうなるだろうか。

亀 kame が距離 $\ell=$ length を深さ n $(\geqq 0)$ だけ turtle_dragon(kame, length, n, sign) することを次のように定義する(sign = $\pm 1$ )。

関数 turtle_dragon(kame, length, n, sign = 1):
演習 右図は深さ 3 のdragon曲線である。 これを描くスクリプト turtle_dragon.py を描いて実行しなさい。 併せて、スクリーンショットも撮りなさい。

dragin曲線は、紙折り曲線(paper folding curves)の仲間として重要で、多くの深い研究がある。 細長く紙を切り出して、半分の長さになるように同じ方向(紙の右端が左端に時計回りに(あるいは反時計回りに)半分に折り畳む操作を繰り返して続ける(一階の操作で紙の長さは $\frac12$ になる)。 $n=0,1,2,3,\dots$ と折り畳み操作を行ってから、その折れ目が直角(90°)になるようにして、紙を開いてみよ。そのときの曲線が turtle_dragon(kame, length, n, +1) または turtle_dragon(kame, length, n, -1) である。

演習 次の関数 tetradragon_curve(kame, length, depth) を使って、カメに軌跡を描かせなさい。 この軌跡と紙折り曲線との関連について、考えなさい。
def tetradragon_curve(kame, length, depth):
    if depth == 0:
        kame.forward(length)
    else:
        kame.left(30)
        tetradragon_curve(kame, length/math.sqrt(3), depth - 1)
        kame.right(120)
        tetradragon_curve(kame, length/math.sqrt(3), depth - 1)
        kame.left(120)
        tetradragon_curve(kame, length/math.sqrt(3), depth - 1)
        kame.right(30)

亀は植物を模倣する

形態学立場での植物(plan)の定義とは何だろうか。 その一つとして、木構造(tree structure)が挙げられる。 根(root)から枝分かれを繰り返して、葉(leaf)に至るという構造で、葉までの道筋は一意で閉路(closed path)を持たない。
The Algorithmic Beauty of Plants』(Przemyslaw Prusinkiewicz and Aristid Lindenmayer, Springer 1991)は、記号言語理論と人工植物生成を関係づけ追求した腐朽の書だ。

次の亀はどのような軌跡を生み出すかを予測することができるだろうか?。 カメの後ずさりメソッド backward を使っている。 このスクリプト turtle_plant.py も深く考えるに値する。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import turtle
#my_turtle = turtle.Turtle()
#my_win = turtle.Screen()

def tree_like_plant(kame, branch_len):
    if branch_len > 5:
        kame.forward(branch_len)
        kame.right(30)
        tree_like_plant(kame, branch_len - 20)
        kame.left(50)
        tree_like_plant(kame, branch_len - 10)
        kame.right(20) # attention! 50 = 30 + 20
        kame.backward(branch_len)

mywin = turtle.Screen()
mykame = turtle.Turtle()
mykame.speed(10)
mykame.color("green")
mykame.penup()
mykame.left(90) # set kame diretion basically to upward
mykame.goto(0, -200)
mykame.pendown()

tree_like_plant(mykame, 100)

mywin.exitonclick()
演習: このスクリプト turtle_plant.py を実行させなさい。 tree_like_plant(kame, branch_len) を大きく変更せずとも、9行目、11,12行目、13,14,15行目に露わレス数字を少しずつ変えるだけでも、カメの軌跡は随分変化する。 そうしながら、関数 tree_like_plant(kame, branch_len) の挙動を理解することがたいへん重要である(たとえば、50 = 30 + 20 とは何か、など)。 併せて、スクリーンショットも撮りなさい。