Upload
others
View
0
Download
0
Embed Size (px)
Citation preview
第2章 繰り返し演算とフラクタル あるルールにしたがって同じ演算を繰り返す繰り返し演算は,差分方程式あるいは再帰
方程式と呼ばれて方程式によって記述される.繰り返し演算には実に多くの応用例があ
り,Mathematicaの応用分野としてたいへん重要である.微分方程式も数値的には差分方程
式として解法するので繰り返し演算の一種であるが,微分方程式に関しては数々の組込み
関数,オプションが用意されているので次章で詳しく述べる.繰り返し演算の応用として
とり上げる重要な分野にフラクタルがある.コンピュータグラフィックスへの応用ばかり
なく,最近は実応用も多くなってきた.このような理由から,本章での扱う主な応用例と
してフラクタルをとり上げる.
<< Graphics`Graphics`H∗DisplayTogether,LogListPlot∗L<< Graphics`SurfaceOfRevolution`H∗SurfaceOfRevolution∗L<< Graphics`MultipleListPlot`H∗MultipleListPlot∗L<< Graphics`Graphics3D`H∗3 次元のグラフィックス∗L<< Geometry`Rotations`H∗回転行列∗L<< ProgrammingInMathematica`IFS`H∗IFS∗L
2.1 再帰方程式
再帰方程式(差分方程式)を解くための関数にRsolve,Nestなどがあるがあ
る.Rsolveで扱える方程式の形にはかなりの制約があるが,簡単な方程式ならば便利で
ある.複雑な演算が必要な対象への応用には数値的な計算を行うNestが欠かせなく,実
用的には最も重要な関数である.まず,Rsolveから始める.
2.1.1 1次元のRSolve
ダイナミックに変動する変数をxn(n = 1, 2, 3, ...)とする.nは等間隔でサンプルした
時の離散時間を表す変数で,ここでは繰り返し数あるいは単に時刻と呼ぶ.今,xnは1階
の再帰方程式
xn+1 = f HxnLに従うと仮定しよう.ここで,関数fはシステムの特性を表す.上記の再帰方程式の解を求
めるための演算操作を考えよう.与えられた初期値x1から初めて,
78
x2 = f Hx1L,x3 = f Hx2L = f 2Hx1L,...などと次々に繰り返せば解が求められる.Rsolveはこのような演算を行う組込み関数で
ある.しかし,解が求められる関数 f には制限があり,決して一般的な方法とは言えない
が,以下に示すような簡単な例ではたいへん便利である.
簡単な例から始めよう.初期値をx1 = 1とした再帰方程式
xn+1 = 0.5 xn + 1
の解は,任意の繰り返し数nに対して
myRS1 = RSolve@8x@n + 1D 0.5 x@nD + 1, x@1D 1<, x@nD, nD88x@nD → −2. 0.5n + 2. 0.5n 0.693147 n<<として求めることができる.Rsolveの使い方は,RSolve[...,x[n],n]の括弧
...の中に解くべき再帰方程式と初期値を与えれる.ただし,=ではなく==を使うこと
に注意しよう.この場合,1階の階差を使っているので,初期値は1個で十分ある.以上の
ようにして求めたxnの値を利用したい場合は,結果が2重の括弧...の中あることを
考慮して,
x@nD ê. First@myRS1D êê Simplify0.5n H−2. + 2. 0.693147 nL
として取り出す.さらに,各nでの値xnを求めるには,たとえば,8x@nD ê. First@myRS1D ê. n → 2,
Table@x@nD ê. First@myRS1D, 8n, 3, 5<D<81.5, 81.75, 1.875, 1.9375<<などとする.前者はnが2の場合,後者はいくつかのnをまとめて求めたい場合である.た
だし,x[2]やx[2]/.First[myRS1]では求まらない.ListPlotが用いて,n = 15まで
プロットすると,
ListPlot@Table@x@nD ê. First@myRS1D, 8n, 1, 15<D,PlotRange → 80, 2.5<,PlotStyle → [email protected], AxesLabel → 8"n", "xn"<D;
2 4 6 8 10 12 14n
0.5
1
1.5
2
2.5xn
となる.
次に,もう少し複雑な2階の再帰方程式
79
xn+2 = f Hxn+1, xnLを考えよう.この場合,右辺には2個の変数があるので,初期値はx1とx2が必要になる
が,1階の再帰方程式と同様に,
x3 = f Hx2, x1L,x4 = f Hx3, x2L = f H f Hx2, x1L, x2L,...として,次々に解を求めることができる.具体的な方程式として,フィボナッチ数列を生
成する方程式
xn+2 = xn+1+ xn
を考えよう.初期値をx1 = 0,x2 = 1とすると,
myFIB = RSolve@8x@n + 2D x@n + 1D + x@nD, x@1D 0, x@2D 1<,x@nD, nD êê Simplify99x@nD →
110
J−I−5 + è!!!5 M J 12I1 + è!!!5 MNn + J 1
2I1 − è!!!5 MNn I5 + è!!!5 MN==
を得る.使い方は,2階の階差を使っているので初期値は2個必要になる以外は,1階の再
帰方程式と変らない.求めた結果をnを横軸にしたグラフで表してみよう.表示するデー
タを予め,
myFIBData = Table@x@nD ê. First@myFIBD, 8n, 2, 15<D;として用意しておく(n = 2からとしたのは,後の都合でx1 = 0を除くためである).そし
て,xnと同時に,
xn+1ÅÅÅÅÅÅÅÅÅÅÅxnも図示する.これは,N = 20として,リスト8x1, x2, ..., xN <を準備すると,上記の数列はリストとしては,
8x2, x3, ..., xN < ê 8x1, x2, ..., xN-1<から計算できる.分母はDrop[myFIBData,1],分子はDrop[myFIBData,-1]で表せる
ので,たとえば,
Drop@8x1, x2, x3, x4<, 1DêDrop@8x1, x2, x3, x4<, −1D9 x2x1
,x3x2
,x4x3
=とすれば,xn+1 ê xnが計算できる.そこで,最後に両者を図示すると,
Show@GraphicsArray@Block@8$DisplayFunction = Identity<,8LogListPlot@myFIBData, PlotRange → All,
PlotStyle → [email protected], AxesLabel → 8"n", "xn"<D,ListPlot@Drop@myFIBData, 1DêDrop@myFIBData, −1D,PlotRange → All, PlotStyle → [email protected],AxesLabel → 8"n", "xn+1êxn"<D<DDD;
80
2 4 6 8 10 12 14n1
510
50100
xn
4 6 8 10 12n
1.2
1.4
1.6
1.8
2xn+1êxn
となる.前者のグラフにおいて,片対数グラフLogListPlotを使って表示すると,ほぼ
直線になっているが,このことはxnがnとともに指数関数的に増加することを意味す
る.後者のグラフから読みとれることは,xn+1 ê xnは一定値に収束していることである.その一定値は,
Limit@H# ê. n → n + 1LêH#L, n → ∞D &@x@nD ê. First@myFIBDD êê FullSimplify
12I1 + è!!!5 M
である.この収束値は黄金比GoldenRatioと呼ばれ,グラフィックスの描画領域を指定
するオプションのデフォルトとして利用されている.
以上のように解析的に解が求められるのは線形な再帰方程式であり,これが非線形にな
ると一般的には適用できず,後で述べるように数値的な方法に譲ることになる.今後の
バージョンアップで適用範囲が広がる可能性はあるが,非線形で解が求まるのは,それに
適した方法が知られている場合で,むしろ例外的と考えられる.その中で注目したい一つ
の例をとり上げよう.後で述べるカオスを生成するロジスティック写像と呼ばれる有名な
再帰方程式で,一般的には数値的に計算することになるが,RSolveで例外的に解け
る.解ける理由は,その解法に有効な変換方法が知られているからである.さて,ロジス
ティック写像は
xn = 4 xn-1H1 - xn-1Lと表される.初期値を変数として,RSolveで求めた解を,
myLogistic@initial_D =
RSolve@8x@n + 1D 4 x@nD H1 − x@nDL, x@0D initial<, x@nD, nDSolve::ifun : 逆関数がSolveで使われているため,求められない解がある可能性があります.解の詳細情報にはReduceをお使いください. 詳細99x@nD →
12H1 − Cos@2n ArcCos@1 − 2 initialDDL==
としよう.警告メッセージが現れる.非線形の場合,複数の解が存在する可能性があ
り,現在のバージョンではこのような場合には対処しきれない.しかし,ロジスティック
81
写像の場合は正確な解が求まっていることは確認できる.実際,my-
Logistic[initial]として実行するか,あるいは初期値を設定しないで,
RSolve@x@n + 1D 4 x@nD H1 − x@nDL, x@nD, nD99x@nD →12
−12Cos@2n C@1DD==
とすると,定数C[1]を用いた一般解が求まる.非線形方程式であることを考えると,ち
ょっとした驚きである.
【問題】上記の方程式で,係数4を3.8など別な値にすると解法できるだろうか,確かめ
よ.
さて,myLogisticを使って計算したデータを表示しよう.ここでは,初期値を少し変
えて,二つの軌道の違いを調べると,
DisplayTogether@ListPlot@Table@x@nD ê. First@myLogistic@#DD, 8n, 0, 50<D,
PlotJoined → True, PlotStyle →
Hue@If@100 #ê π 1, 1, 0.6DD, H∗二つの軌道に彩色∗LFrame → True, FrameLabel → 8"n", "xn"<D & ê@8N@π ê 100D, N@π ê101D<, H∗二つの初期値∗L
PlotLabel → StyleForm@"ロジステック写像"DD;
0 10 20 30 40 50n
0
0.2
0.4
0.6
0.8
1
xn
ロジステック写像
となる.このグラフの意義は後章やカオスに関する文献を参照されたい.以上のようにし
て求めたロジスティック写像の解に関して以下の2点を注意しよう.
(1)初期値
上図に載せた2つの軌道の初期値の差はわずかで,
N@π ê 100D − N@π ê 101D0.000311049
である.それにもかかわらず,軌道の差,xn - xn '(x1 = p ê100,x1 ' = p ê 101)はnが10に
もなれば目視で分かる程度にまで大きくなる.この性質がロジスティック写像のカオスと
82
しての重要な特徴である.
(2)RSolveが有効な理由
先に,係数4を3.8など別な値にすると解けないと述べたが,このことは係数4の場合は
特殊な方法を用いて解いていることを示唆する.myLogisticあるいはRSolveの結果を
見ると,三角関数を使っていることが分かる.実際,初期値を
x1 = sin2Hp bLと変換する.ただし,カオスになるためにはbは無理数でなければならない.次の時刻で
の値は,
x2 = 4 x1H1 - x1L = 4 sin2Hp bL cos2Hp bL = sin2H2 p bLとなり,一般の時刻nでは
xn = sin2H2n p bLとなることが示せる.このような特殊な方法が利用できる場合は,非線形な方程式でも解
析解は求まるので,RSolveには今後も解ける方程式が追加される可能性はあるだろう.
もう1つ例を述べておく.2階の再帰方程式
xn = xn+1 + xn-1
と与えられているとする.方程式の解法と図示をまとめて実行すると,
ListPlot@Table@Evaluate@x@nD ê. RSolve@8x@nD == x@n − 1D + x@n + 1D,
x@0D 0, x@1D 1<, x, nD@@1DDD, 8n, 0, 20<D,PlotJoined → True, PlotStyle → [email protected],AxesLabel → 8"n", "xn"<D;
5 10 15 20n
−1
−0.5
0.5
1xn
となる.尚,ここではEvaluateが必要なことに注意しよう.
2.1.2 フラクタル次元
応用としてフラクタル次元を求める数式をとり上げよう.フラクタルの特徴は入れ子構
造になっていることで,その特徴を数式で表すと,
f HnL = b f I nÅÅÅÅa Mとなる.ここで, f はフラクタルを量的に表現したもので,図形の場合,変数nを1次元の
長さとすると, f は図形の周辺の長さ,体積などを表す.上記の方程式は,nをn ê aにス
83
ケール変換して対象を眺めても f の特徴は変化せず,b倍すると元の f に等しくなること意
味する.このような性質を持つのがフラクタルである.
さて,上記の方程式は通常の再帰方程式に変換できるが,ここでは直接,Rsolveで解
いてみよう.実際,一般解は
powerS = First@RSolve@8 x@nD b x@nêaD<, x@nD, nDD9x@nD → b−1+ Log@nDLog@aD C@1D=
とすれば求められる.尚,フラクタル次元Dは
f º n-D
と定義されるので,nが大きい極限でlog f º -D log nからDが決まる.この場合,
Limit@PowerExpand@Log@x@nD ê. powerSDDê Log@nD, n → ∞DLog@bDLog@aD
から,フラクタル次元は
D = -log bÅÅÅÅÅÅÅÅÅÅÅÅlog a
となる.尚,通常a < 1,b > 1なので,D > 0である.
2.1.3 2次元のRSolve
2次元に拡張した場合の再帰方程式に挑戦してみよう.挑戦といったのは,1次元の方程
式以上に解が求まる状況が限られるからである.2次元の場合,繰り返し数,つまり添え
字が2種類必要になる.最初の例は,
xi, j = xi+1, j+1 + xi-1, j-1
である.今,8i, j<座標にセルを置き,そのセルに変数xi, jを割り当てる.この方程式では
対角線上に位置するセルのみを使っている.1次元の場合は先に述べたxn = xn+1 + xn-1に
相当する.したがって,Rsolveで解が得られることが期待される.実際,
RSolve@x@i, jD x@i + 1, j + 1D + x@i − 1, j − 1D, x@i, jD, 8i, j<D99x@i, jD → CosA i π3
E C@1D@−i + jD + SinA i π3
E C@2D@−i + jD==から,解は
xi, j = Jc1H j - iL cos p iÅÅÅÅÅÅÅ3 + c2H j - iL sin p iÅÅÅÅÅÅÅ3 Nのように, j - iの値にのみに依存する未定定数を2個用いて表せる.このような再帰方程
式はセルオートマトンモデルとして知られているが,式の右辺の形をいろいろ変えて実行
すると分かるように,線形であっても解が求まるのはむしろ例外的である.たとえば,
RSolve@x@i, jD x@i + 1, j + 1D + x@i − 1, j − 1D + x@i − 1, jD,x@i, jD, 8i, j<D
84
RSolve@x@i, jD x@−1 + i, −1 + jD + x@−1 + i, jD + x@1 + i, 1 + jD,x@i, jD, 8i, j<D
としても解は示されない.
最初に示した解法可能な例を用いて,球解とその解のListContourPlotを用いた表示
をまとめて実行すると,
ListContourPlot@Table@Evaluate@x@i, jD ê. First@RSolve@x@i, jD
x@i + 1, j + 1D + x@i − 1, j − 1D, x@i, jD, 8i, j<DD ê.8C@1D@pt_D → pt, C@2D@pt_D → Cos@ptD<D,8i, 50<, 8j, 50<D, FrameLabel → 8"x", "y"<D;
10 20 30 40 50
x
10
20
30
40
50
y
のようになる.ここで,8C@1D@pt_D → pt, C@2D@pt_D → Cos@ptD<によって,境界条件になっている2個の未定定数に値を代入している.
Rsolveは,以上に示したように,対象が簡単な場合には有効であるが,一般的には数
値的に再帰方程式を求めることになる.
2.2 コンパイル
Mathematicaの各種の計算に使われているコンパイルCompileは,数値積分を実行する
関数NIntegrate等に限らず,Plot等の描画関数にも使われているが,これまで特に意
識することはなかった.しかし,ユーザが作成した関数やプログラム,特に繰り返し計算
を多用する場合,Compileはプログラムの演算速度を高速化し,しかも繰り返し回数が多
い方がより効果的である.カオスやフラクタルなどの複雑系ではCompileは不可欠であ
る.
2.2.1 コンパイルとは
85
コンパイルがどのような役割を果たすかを調べるため,グラフィックスにおけるコンパ
イルを調べてみよう.たとえば,<<Graphics`SurfaceOfRevolution`をロードして
おき,SurfaceOfRevolutionのオプションを調べると,
Options@SurfaceOfRevolution, CompiledD8Compiled → True<と,オプションCompileがあり,そのデフォルトがTrueとなっていることが分か
る.SurfaceOfRevolutionに限らないが,グラフィックスではデフォルトでCom-
pileを使っている.では,敢えてコンパイルを使わないようにCompiledをFalseとすれ
ば,どの程度効率が悪くなるか調べよう.はっきりした効果を現れるように,オプション
PlotPointsを100にしておくと,
Timing@SurfaceOfRevolution@8 Sin@uD, u2<, 8u, 0, 2 π<,PlotPoints → 100, BoxRatios → 81, 1, 2<, DisplayFunction →
Identity, Compiled → #DD & ê@ 8False, True<880.703 Second, Graphics3D <, 80.297 Second, Graphics3D <<となり,コンパイルの効果が確認できる.ただし,初めて実行する時よりも2回目以降の
方がコンパイルの効果が顕著になる.初めて実行した段階では,コンパイルしたコードを
生成するだけであるからである.なお,グラフィックスによっては必ずしもコンパイルの
効果が現れない場合もある.
Cos等の数学関数はもともとコンパイルされていない.では,このような関数のコンパ
イルしたコードを作成するためのは,どのようにすべきか考えてみよう.コンパイルした
コードを生成するには特別な関数Compileを用いるが,その使用方法は通常の関数
Functionと同じように定義できる.実際,FunctionをCompileと変えるだけで済
む.あらゆる計算にCompileが使えるわけではないが,Compileでどの程度計算が早く
なるか,まずは,簡単な例8f, cf< = 8Function@x, Cos@xDD, Compile@x, Cos@xDD<8Function@x, Cos@xDD,CompiledFunction@8x<, Cos@xD, −CompiledCode−D<
を用いて調べよう. f は通常の関数(単にCos[x]と同じ)であるが,関数cf はコンパイ
ルした関数であることは-CompiledCode-からも分かる.関数があまりに単純なの
で,多くのデータを用いて計算させないとCompileの効果が現れない.ここで
は,Range[100000]に対する計算時間を合計して,8Plus @@ HFirst@Timing@f@#DDD & ê@ Range@100000DL,Plus @@ HFirst@Timing@cf@#DDD & ê@ Range@100000DL<80.78 Second, 0.36 Second<
86
とすると,高速になっていることが確認できる.
ユーザが定義した任意の関数をコンパイルするのも,基本的には上記の例と同様であ
る.初めに,Compileが関数の計算にどのような構造を持ち込むか調べよう.今,関数
f HxLをClear@fD;f@x_D := x2
と定義する.最初にClear[f]とした理由は明らかであろう.f[x]のコンパイル関数
は,Cosの場合と同様に
myC = Compile@8x<, f@xDDCompiledFunction@8x<, f@xD, −CompiledCode−D
と定義する.コンパイル関数の定義には:=は使わない.遅延型割り当てを用いると,関数
が呼び出された時に初めてコンパイルしたコードを生成するからである.括弧...は複
数の変数がある場合には不可欠である.変数に値を与えて,実行すると,
myC@#D & ê@ 81.0, 2.5, a<CompiledFunction::cfsa :
位置1の引数aはmachine−size real numberであるべきです. 詳細81., 6.25, a2<となる.数値に対しては問題なく実行するが,シンボルの場合は,コンパイルできないと
の警告メッセージがでるものの結果は正しい.これは,変数の型の問題で,コンパイルの
性質上,変数の型としてシンボルをとることができない(コンパイルしないで計算す
る).予め変数の型を指定するためには,たとえば,Compile[x,_Real,f[x]],あ
るいはCompile[x,_Comples,f[x]]などとする.myCにはf[x]がそのまま現れてい
るが,x2は計算される.実際,Applyを用いると,
Apply@Compile, 8x, f@xD<DCompiledFunction@8x<, x2, −CompiledCode−D
と,x2が陽に現れる.また,どのようなコードが生成されたかは,FullFormでチェック
できる.尚,xを局所変数として,
Module@8x<, Compile @@ 88x<, f@xD<DCompiledFunction@8x$455<, x$4552, −CompiledCode−D
とした方がMathematicaらしいプログラムになる.ここで,Moduleを使っているので,内
部で自動的に付けられた変数x$...が現れる.変数に値を与えるには,通常の関数と同
じようにして,
87
Module@8x<, Compile @@ 88x<, f@xD<D@#D & ê@ Range@5D81., 4., 9., 16., 25.<とすればよい.
もう1つ例を示しておこう.xyを計算しよう.関数を
Clear[ncPower,cPower];
ncPower[x_Real,y_Integer]:=xy ;
cPower=Compile[x,_Real,y,_Integer,xy ];
と定義する.ただし,xを実数,yを整数としている.実行例は,たとえば,[email protected], 3D, [email protected], 3.2D,[email protected], 3D, cPower@2, 3.2D<CompiledFunction::cfsa :
位置2の引数3.2`はmachine−size integerであるべきです. 詳細88., ncPower@2., 3.2D, 8., 9.18959<となるが,ここで面白いのは,ncPowerでは変数の型が合わないと計算されないが,先に
述べた例のように,cPowerでは警告メッセージがでるものの正しい結果が計算されてい
る.
さて,計算速度を比べてみよう.8First@Timing@[email protected], #D & ê@ Range@−3000, 1000D;DD,First@Timing@[email protected], #D & ê@ Range@−3000, 1000D;DD<80.109 Second, 0.016 Second<
となり,予想通りの結果である.さて,この場合,新たに
myPower = Compile@88x, _Real<<, Hx#1 &L ê@ Range@−3000, 1000DD;と定義し,前と同じ計算を実行すると,
Timing[myPower[2.0];]80. Second, Null<となって,更に高速に計算できる.
【問題】なぜ高速になるのか,その理由を考えよ.
最後に,
Clear@ncPower, cPower, myPowerD;としておこう.
2.2.2 いくつかの例
88
Mathematicaはたいへん柔軟なプログラム環境を提供しているため,同じ計算をするにも
複数の方法があり,場合によってはどの方法を選ぶべきか迷うことがしばしばある.たと
えば,リスト8x1, x2, ..., xn<の各要素の2乗和
x12 + x2
2 +, ..., +xn2 = 8x1, x2, ..., xn<.8x1, x2, ..., xn<
を求めたい場合,以下のような関数が考えられる.
Clear@fD;f@1D@x_D := Sum@x@@iDD2, 8i, Length@xD<D;f@2D@x_D := Plus @@ x2;
f@3D@x_D := x.x;
f@4D = #.# &;
f@5D = Compile@88x, _Real, 1<<, x.xD; H∗xはリスト∗L【問題】上記の各関数の定義で,=と:=を使い分けている理由を考えよ.
最初のf[1]は,各要素x[[i]]を2乗して和を求めるという直感的な関数である.Mathe-
maticaらしい関数としては,f[2]が好ましい.f[3]は内積,f[4]は純関数,f[5]はコン
パイルを用いた方法である.x,_Real,1によって,1次元リストであることを前提
にする.このように表面的には異なった各種の方法があるが,内部でコンパイルを用いて
いるかどうかは分かりづらい.つまり,f[5]が1番早く計算できるとは早急には決められ
ない.そこで,これらの方法を用いて計算時間を調べよう.まず,乱数のリストを
ranDate@n_D :=
Table@Random@D, 8n<, 8Random@Integer, 81, n<D<Dと定義しておき,たとえば,
ranEXAM = [email protected]<, 80.831854, 0.486411<, 80.250406, 0.64249<<と準備する.これは,要素が乱数で,しかも要素数自体もランダムにした3個のリストで
ある.この3個のリストに対してf[1]で2乗和を求めると,
Timing@f@1D ê@ ranEXAMD80. Second, 80.787918, 0.928577, 0.475497<<となる.すべての関数に対して計算するためには,
Timing@f@#D ê@ ranEXAMD & ê@ Range@5D880. Second, 80.787918, 0.928577, 0.475497<<,80. Second, 80.787918, 0.928577, 0.475497<<,80. Second, 80.787918, 0.928577, 0.475497<<,80. Second, 80.787918, 0.928577, 0.475497<<,80. Second, 80.787918, 0.928577, 0.475497<<<
89
とすればよい.ここでは,時間だけを調べたいので,
First@Timing@f@#D ê@ ranEXAMDD & ê@ Range@5D ê. Second → 180., 0., 0., 0., 0.<とする.このような小規模のリストだと0(秒)となり比較できないので,もっと大きな
リストを使って比較しよう.そこで,以上のことをまとめ,
ranTime@n_D :=
First@Timing@f@#D ê@ ranDate@nDDD & ê@ Range@5D ê. Second → 1
とする.そして,nをいろいろ変えると,
SeedRandom@1349D;MultipleListPlot@Transpose@Table@ranTime@nD, 8n, 500, 1000, 100<DD,AxesLabel → 8"n", "CPU time"<,PlotLegend → 8"f1", "f2", "f3", "f4", "f5"<,Ticks → 8881, "500"<, 82, "600"<,83, "700"<, 84, "800"<, 85, "900"<<, Automatic<,PlotJoined → True, PlotRange → AllD;
600 700 800 900n
0.5
1
1.5
2
2.5CPU time
f5
f4
f3
f2
f1
となる.この結果から分かることは,和を求めるという直感的な関数f[1]が最も効率が
悪く,またf[2]も安定しない.乱数にもよるが,内積を用いたf[3],コンパイルを指定
した関数f[5]と同程度に純関数を用いたf[4]も効率がよい.最後に,
Clear@fD;としておこう.
コンパイルした関数をコンパイルすると,どうなるだろうか.
Clear@myPower, myPower2, myPower3DmyPower = Compile@8x, y<, xyDmyPower2 = Compile@8x, y<, Do@myPower@x, yD, 8100<DDCompiledFunction@8x, y<, xy, −CompiledCode−DCompiledFunction@8x, y<,Do@myPower@x, yD, 8100<D, −CompiledCode−D
90
myPowerはコンパイルされ,そのコードはメモリに置かれる.myPower2をそれを100回
参照する.以下のmyPower3はmyPowerをそのまま書いた関数で,
myPower3 = Compile@8x, y<, xy; xy; xy; xy; xy; xy; xy; xy; xy; xy;
xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy;
xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy;
xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy;
xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy;
xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy;
xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy; xy;D;である.計算時間を比較すると,
Timing@Do@#@2., 3.D, 8100<DD & ê@ 8myPower2, myPower3<880.094 Second, Null<, 80. Second, Null<<となる.結果は,myPower3が有利であることを示している.
2.2.3 繰り返し計算の高速化
Compileの本格的な使い方を述べよう.しばしば用いる例として,方程式
f HzL = 0
の根を求めるニュートンの公式を考えよう.根は一般に複素数である.ニュートンの公式
は初期値zH1Lから初めて, zHnL = zHn - 1L -
f HzHn-1LLÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅf ' HzHn-1LLにしたがって,zH2L,zH3L等と順に繰り返して求めていく.zHnLはn回目の繰り返しで求ま
った根の近似値である.この例を用いてCompileの具体的な使い方,その効果を示そ
う.ここでは,具体例として,n乗根を求める
f HzL = zn - 1
をとり上げる.変数は繰り返し数nと初期値zH1Lである.Compileを使う通常のプログラムでは,
cNewton = Compile@88n, _Integer<, 8init, _Complex<<,FixedPoint@# − H#n − 1LêHn #n−1L &, init,
20, SameTest → HAbs@#1 − #2D < 0.01 &LDD;と記述する.FixedPointにおける終了条件は,繰り返し数が20になるか,あるいは
SameTestで指定したように,差の絶対値 » zHnL - zHn - 1L »が0.01より小さくなるまで繰り
返す.初期値を変えていくかの計算例を示すと,
Chop@8cNewton@5, 0.5D, cNewton@5, 0.5 + D<DClear@cNewtonDH∗cNewtonをクリア−∗L;81.0002, 0.309096 + 0.95115 <
91
となる.Chopは0に近い値を0にする関数であるが,ここでは小さな値の虚部を0にしてい
る.
後で詳しく述べるNestとCompileを組み合せた例を挙げておく.方程式
xn = sin H4 xn-1H1 - xn-1LLの解を求めよう.sinの変数は既に述べたロジスティック写像である.まず,コンパイルな
しで,
ncSinLogistic@n_Integer, init_RealD :=
Nest@Sin@4 #1 H1 − #1LD &, init, nDと定義して,実行すると,
ncTimeSinLogistic@n_D :=
Timing@ncSinLogistic@n, N@π ê10, 300DDD;ncTimeSinLogistic@100DH∗n=100 として実行∗L80.093 Second,0.50904344611527032409354700614928143676563088679940514663734382436056208120231682622803107016231258991640753913768
255786919847402662665987889825903175259608284908480151713637374998470592924808774581459481399418173301212481358600
42323054474262346353939591191<となる.これを,コンパイル化した関数で記述すると,
cSinLogistic = Compile@88n, _Integer<, 8init, _Real<<,Module@8x = init<, Nest@Sin@4 #1 H1 − #1LD &, x, nDDD
CompiledFunction@8n, init<, Block@8$$1325<, Module@8x = init<,Nest@H$$1325 = #1; Sin@4 $$1325 H1 − $$1325LDL &, x, nDDD,
−CompiledCode−Dとなる.実行すると,
cTimeSinLogistic@n_D :=
Timing@cSinLogistic@n, N@π ê10, 300DDD;cTimeSinLogistic@100DH∗n=100 として実行∗L80. Second, 0.509043<
となって,相当早く計算できる.いろいろなnに対して計算して比較しよう.nを10から
300まで変化させると,
92
MultipleListPlot@Table@8n, First@ncTimeSinLogistic@nDD ê. Second → 1<,8n, 10, 300, 20<D,Table@8n, First@cTimeSinLogistic@nDD ê. Second → 1<,8n, 10, 300, 20<D,PlotJoined → True, PlotRange → 80, 0.25<,AxesLabel → 8"n", "CPU time"<,PlotLegend → 8"非コンパイル", "コンパイル"<,SymbolShape →8PlotSymbol@BoxD, MakeSymbol@RegularPolygon@5, 3DD<D;
50 100150200250n
0.05
0.1
0.15
0.2
0.25CPU time
コンパイル
非コンパ
となり,コンパイルの効果がはっきり分かる.ここで,計算時間にはSecondがついてい
るので,Second→1として数値に直して表示している.最後に,
Clear@ncTimeSinLogistic, cTimeSinLogisticD;とする.
2.3 繰り返し演算
2.3.1 計算手順
再帰方程式xn = f Hxn-1Lを,ユーザが定義した関数を用いて解を求める手続き考えよう.x1から初めて,x2 = f Hx1L,x3 = f Hx2Lなどと繰り返して計算する.いま,
x@1D = initial;
x@n_D := f@x@n − 1DDと定義する.すると,たとえば,x4は,x[1]=initialから,
x@4Df@f@f@initialDDD
となることから分かるように,3回関数 f をネスト(入れ子構造になっているので,このよ
うな繰り返し計算をネストと呼ぶ)している.この段階でx4が具体的にどのように表現さ
れているかを,??またはInformationを用いてそのシンボルに関する保存されている情
報を確かめると,
93
?? x
Global`x
x@1D = initial
x@n_D := f@x@n − 1DDとなっている.つまり,初期値と再帰方程式があるが,x4を求める途中で計算したはずの
x3,x2は記憶されていない.そうすると,再度x4が必要になると,x4のみならず,再び
x3,x2を計算しなければならないことになる.これでは効率が悪い.既に計算した値はメ
モリに記憶しておけば,必要になった時にそれらの値を参照するだけで済み,新たに計算
する必要がなくなり,計算が高速になるはずである.
:=は,以上のように関数の定義に使われるが,この記号からはその機能は分かり難
い.特に,Mathematica内部でのメモリの使い方には十分慣れておく必要がある.上で見て
きたように計算自体は問題ないが,計算済みの値をメモリの保存するために,
xs@1D = initial;
xs@n_D := xs@nD = f@xs@n − 1DDと関数を定義する.前の例との違いは,途中にxs[n]=が入っていることである.つま
り,xs[n]=f[xs[n-1]]とすることで,f[xs[n-1]]の値がメモリにxs[n]として保存
されることになる.実際,
xs@3Df@f@initialDD
に関する情報を見ると,先に定義した方程式と違って,
?? xs
Global`xs
xs@1D = initial
xs@2D = f@initialDxs@3D = f@f@initialDDxs@n_D := xs@nD = f@xs@n − 1DD
から,途中で計算したx3,x2は記憶さている事が分かる.最初に示した
x@n _D := f@x@n − 1DDによると,たとえば,x3 = f Hx2Lを計算する場合,計算済みの値x2を用いるのではなく,再び,x2 = f Hx1Lを計算する.つまり,無駄なことをしている.一方,xs@n _D := xs@nD = f@xs@n − 1DDのように定義すると,同じx3 = f Hx2Lを計
94
算する場合にも,計算済みの値x2をメモリから読み出しているだけある.このこと
は,Matehmaticaでプログラムを記述する時のもっとも重要な使い方の一つで,必ずマス
ターしておきたい.
さて,xsによってどのようなルールが得られたかは,DownValuesを用いて,
DownValues@xsD8HoldPattern@xs@1DD initial,HoldPattern@xs@2DD f@initialD,HoldPattern@xs@3DD f@f@initialDD,HoldPattern@xs@n_DD Hxs@nD = f@xs@n − 1DDL<
から確認できる.したがって,そのようなルールの個数は,
Length@DownValues@xsDD4
個ある.
実際,高速な計算が可能になることを確かめるため,具体的な計算時間を比較しよ
う.いま,関数として,
initial = Random@D;f@x_D := 1.0 + 1 êx
を考える.繰り返し数を多くするため,$RecursionLimitのデフォルト(256)を
$RecursionLimit = 1024;
変更しておく.すると,計算時間はAbsoluteTimingを用いると,
AbsoluteTiming@#@1000DD & ê@ 8x, xs<880.0625000 Second, 1.61803<, 80.1093750 Second, 1.61803<<となり,それほど差はない.初めて計算する時はどちらの方法を使っても当然ほとのど変
らない.もう一度同じ計算をすると,
AbsoluteTiming@#@1000DD & ê@ 8x, xs<880.0468750 Second, 1.61803<, 80. Second, 1.61803<<となり,xsの計算時間は0になる.これは,既に計算済みの値を参照しているだけである
からである.
最後に,繰り返し回数をいろいろ変えて,計算時間を比較しよう.
95
MultipleListPlot@Table@8i, First@AbsoluteTiming@x@iD;D ê. Second → 1D<,8i, 1, 1000, 100<D,Table@8i, First@AbsoluteTiming@xs@iD;D ê. Second → 1D<,8i, 1, 1000, 100<D,PlotJoined → True, PlotRange → All,
AxesLabel → 8"i", "CPU time"<,PlotLegend → 8StyleForm@"xの計算時間", FontSize → 9D,StyleForm@"xsの計算時間", FontSize → 9D<,
LegendPosition → 81.1, 0.0<, LegendSize → 81.4, 0.5<,LegendTextSpace → 3D;
200400600800i
0.010.020.030.04
CPU time
xsの計算時間
xの計算時間
となる.xsの計算時間はやはりほとんど0である.
以上のことからから,再帰方程式ではデータがメモリに保存されるので,処理を終了す
る場合は一旦カーネルを終了するか,引き続き使うのであれば,保存したシンボルは消去
した方が無難である.メモリから削除するためには,
Remove@f, x, xs, initialD;とする.削除しておかないと,後で同じシンボルxを用いて計算したい時,思わぬエラー
がでる場合がある.さらに,
$RecursionLimit = 256;
と戻しておこう.このような繰り返し演算は,場合によっては後で述べるようにもっと簡
単に行える.
ここで,階乗を計算する
facDynamic@0D = 1;
facDynamic@n_Integer?PositiveD :=
facDynamic@nD = n facDynamic@n − 1D;をとり上げ,トレースして,計算過程の詳細を確認しよう.3の階乗は
facDynamic@3D6
である.この後,facDynamic[5]を計算しようとする.これまで説明してきたよう
に,facDynamic[3],facDynamic[2]は計算済の値を参照するだけで,新たに計算す
96
べき値はfacDynamic[4]である.計算の各ステップでどのような処理を行っているか
は,Traceで調べられる.実際,
Trace@facDynamic@5D, facDynamic@_DD8facDynamic@5D, 88facDynamic@4D, 88facDynamic@3D<<<<<とすればよい.ここで,facDynamic[_]によって,マッチングする情報を引き出してい
る.ついでに説明しておくと,Traceには以下のような使用方法もある.
Trace@facDynamic@30D, facDynamic@n_ ê; n > 24DD8facDynamic@30D,88facDynamic@29D, 88facDynamic@28D, 88facDynamic@27D,88facDynamic@26D, 88facDynamic@25D<<<<<<<<<<<この意味は説明しなくても理解できると思う.最後に,
Remove@facDynamicD;としておこう.
【問題】一旦,facDynamicを取り除き,fibDynamic[20]を計算して,続いて
fibDynamic[40]をトレースしてみよう.
2.3.2 1次元Nest
再帰方程式による繰り返し演算は数学的な表現に等しく,馴染深く感じるかもしれない
が,もっと便利でMathematicaらしい演算記法がある.NestおよびNestList等は,Mathe-
maticaの中でも頻繁に使われる重要なコマンドである.前節と同じように,xn = f Hxn-1Lの解を求める操作を考えよう.たとえば初期値をxとして,x5を求めたいのであれば,
Nest@f, x, 5Df@f@f@f@f@xDDDDD
とすればよい.純関数でも同じ形式で記述できる.また,通常の関数を用いて,
Nest@Function@x, f@xDD, x, 5Df@f@f@f@f@xDDDDD
としても同じ演算を実行する.ただし,関数としての変数xと初期値xの区別に注意しよ
う.さらに,純関数の引数#あるいは#1を用いると,
Nest@f@#D &, x, 5Df@f@f@f@f@xDDDDD
としても,同じ演算ができる.たとえば,関数 f としてcosにすると,通常の関数として
も,純関数としても,
97
Nest[Cos,x,5],
Nest[Function[x,Cos[x]],x,5],
Nest[Cos[#]&,x,5]8Cos@Cos@Cos@Cos@Cos@xDDDDD,Cos@Cos@Cos@Cos@Cos@xDDDDD, Cos@Cos@Cos@Cos@Cos@xDDDDD<
はすべて同じ結果を与える.数値的に求めたい場合は,たとえば,
With@8x = π ê3.<,8Nest@Cos, x, 5D,Nest@Function@x, Cos@xDD, x, 5D,Nest@Cos@#D &, x, 5D<D80.694778, 0.694778, 0.694778<
となる.初期値からx5まですべて求めたいならば,
NestList@f, x, 5D8x, f@xD, f@f@xDD, f@f@f@xDDD,f@f@f@f@xDDDD, f@f@f@f@f@xDDDDD<
とするだけで済む.ここで注意したいのは,リストの要素数は初期値を含め6となってい
ることである.純関数での記述の仕方はNestと同じ形式で記述できる.
例として,0からxまで積分するŸ0x# x(Integrate[#, x, 0, x]と同じ)を含
む再帰方程式
xn = Ÿ0xxn-1HxL d x
を,Nestを使って求めよう.初期値をx0HxL = 1とすると,
NestList[(Ÿ0x# x)&,1,5]91, x,x2
2,
x3
6,
x4
24,
x5
120=
となる.リストの最初の要素は初期値の1,次の要素はŸ0x1 d x = x,更に次の要素はŸ0
xx d x = x2 ë2等となる.
次に,
xn = 1ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ1+xn-1を考えると,その値
NestList@1 êH1 + #L &, x, 3D9x, 11 + x
,1
1 + 11+x
,1
1 + 11+ 1
1+x
=は連分数を与える.特に,繰り返し数を大きく取ると
Nest@1êH1 + #L &, 1, 100D êê N0.618034
98
となって黄金比の逆数0.618034に近づいていく.尚,これに関する応用として,各抵抗値
が1の無限に続く抵抗回路の全抵抗を求める問題がある.NestListで求めた値をグラフ
にすると,
ListPlot@NestList@1êH1 + #L &, 0.1, 10D,PlotRange → 880, 11<, 80, 1<<,PlotJoined → True, AxesLabel → 8"n", "xn"<D;
2 4 6 8 10n
0.2
0.4
0.6
0.8
1xn
となる.ただし,横軸の値nの1が初期値x0,2がx1に対応していることに注意された
い.もし,添え字通りに表示したければ,NestListにRange[0,10]を用いて,2次元配
列にしてから,
ListPlot@Transpose@8Range@0, 10D, NestList@1êH1 + #L &, 0.1, 10D<D,PlotRange → 880, 11<, 80, 1<<, PlotJoined → True,
AxesLabel → 8"n", "xn"<D;
2 4 6 8 10n
0.2
0.4
0.6
0.8
1xn
とプロットしよう.このような添え字の変更方法はしばしば用いるので慣れておきた
い.nが大きくなると,次第に,
x* = 1ÅÅÅÅÅÅÅÅÅÅÅÅ1+x*
つまり,
Solve@x 1 êH1 + xL, xD êê N88x → −1.61803<, 8x → 0.618034<<の解に収束していく(正の解が黄金比の逆数1/GoldenRatioである).
2次元に拡張して,再帰方程式の解の図を描こう.
NestList@Log, 1.0 + , 3D
99
81. + , 0.346574 + 0.785398 ,
−0.152608 + 1.15522 , 0.152945 + 1.70214 <として,初期値1 + iにlogを適用する.15回繰り返して,各要素の実部と虚部をそれぞ
れ,横軸,縦軸にとって表示すると,
ListPlot@8Re@#D, Im@#D< & ê@ NestList@Log, 1.0 + , 15D,AspectRatio → Automatic, PlotJoined → True, PlotRange → AllD;
0.2 0.4 0.6 0.8 1
0.8
1.2
1.4
1.6
となる.繰り返し回数を増やと,図形の中心に向かって巻きついていく.図形の中心の値
は,
Solve@Log@xD x, xD êê N88x → 0.318132 − 1.33724 <<である.
さて,1次元ランダムウォーク
xn = xn-1 + xn
を考えよう.ここでは,xnは互いに独立なノイズである.この繰り返し計算を実行するの
に,これまでの例と同様にNestListを使う.確率によって右に+1か-1に移動すること
を考慮し,10回ランダムウォークした結果を合せて表示すると,
With@8dir = 81, −1<<, H∗右方向と左方向∗LDisplayTogether@Table@H∗10 回ランダウォ-クさせる∗LListPlot@NestList@# + dir@@Random@Integer, 81, 2<DDD &,0, 200D, H∗200 歩のランダウォ−ク∗L
PlotJoined → True, AxesLabel → 8"n", "xn"<D, 810<DDD;
100
50 100 150 200n
−20
−10
10
20
xn
となる.ここで,Random[Integer,1,2]は等確率で1か2を与えるので,dir[[-
Random[Integer,1,2]]では+1か-1かが等確率で選ばれることになる.純関数
#+dir[[Random[Integer,1,2]]にNestListを適用することで,初期値0か
ら,0 + (1か-1),0 + (1か-1)+ (1か-1)などど順にデータを生成する.
次の例として,
xn = è!!!!!!!!!!!xn-1
の固定点,つまり,x0,x1,x2 ...と求めていった時に,変化しなくなった値を調べよ
う.固定点は明らかに,
x* =è!!!!!!!x*
なる方程式
SolveAx ==è!!!!x , xE88x → 0<, 8x → 1<<
の解である.たとえば,x0 = 0.3から初めて,いくつかデータを生成すると,
NestList@Sqrt, 0.3, 5D80.3, 0.547723, 0.740083, 0.860281, 0.927513, 0.963075<となって,固定点1に近づいていくことが確かめられる.
固定点だけを調べたいならば,FixedPointが利用できる.この関数を使う時の注意と
しては,事前に固定点が存在するかどうか分からない,あるいは収束が遅い時は,繰り返
しを終了するための条件を付加する必要があることである.ただし,この場合は条件なし
でも,
FixedPoint@Sqrt, 0.3D1.
と正確な固定点が求まる.実際,8#, Sqrt@#D< &@FixedPoint@Sqrt, 0.3D81., 1.<となるように,x =
è!!!x の根であることが分かる.NestListと異なり,関数Sqrtを適用
する回数を設定していないのも係わらず,なぜ,正確な固定点が求まったのだろうか.実
101
際には,収束のための条件がデフォルトとして設定されているのである.今,収束条件を» xn - xn-1 » § 0.1,つまり,連続した値の差が0.1以内になれば収束したと見なし,その値
を固定点としたいならば,
FixedPoint@Sqrt, 0.3, SameTest → HAbs@#1 − #2D < 0.1 &LD0.927513
とする.結果は1より若干小さくなる.このように,SameTestは収束条件を純関数とし
て与えている.SameTestを省略した場合は,先の示したようにデフォルトのAuto-
maticとなり,収束条件が機械精度$MachineEpsilonで与えられ,
FixedPoint@Sqrt, 0.3,
SameTest → HAbs@#1 − #2D < $MachineEpsilon &LD1.
となる.あるいは,これと同じ働きは,
FixedPoint@Sqrt, 0.3, SameTest → EqualD1.
とすることでも得られる.以上のように,収束を伴った計算では,常に精度を考慮しなけ
ればならない.繰り返し回数を条件とするなら,たとえばn = 10として,
FixedPoint@Sqrt, 0.3, 10D0.998825
とする.さらに,精度と繰り返し回数の両者を条件として与えたいならば,
FixedPoint@Sqrt, 0.3, 10, SameTest → HAbs@#1 − #2D < 0.1 &LD0.927513
とする.つまり,精度か繰り返し回数かのどちらかの条件が満たされれば終了する.この
場合は,先の結果から分かるように,終了は精度の方で決まっている.初期値から固定点
に至るデータをすべて出したいなら,FixedPointListを用いて,
FixedPointList@Sqrt, 0.3D80.3, 0.547723, 0.740083, 0.860281, 0.927513, 0.963075,0.981364, 0.990638, 0.995308, 0.997651, 0.998825, 0.999412,
0.999706, 0.999853, 0.999927, 0.999963, 0.999982,0.999991, 0.999995, 0.999998, 0.999999, 0.999999, 1., 1.,1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.<とする.1.が何回も並んでいるのは,$MachineEpsilonで与えられる精度まで繰り返し
ているからである.上記リストの最後の要素は%[[-1]]あるいはLast[%]で表せるの
で,誤差は,
102
1.0 − %@@−1DD1.11022×10−16
となり,これから収束した値の精度が確認できる.
FixedPointListを使って既にとり上げた
xn = 1ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ1+xn-1の固定点を求めよう.収束した値の誤差も同時に求めると,
FixedPointList@1 êH1 + #L &, 0.5, 50,
SameTest → HAbs@#2 − #1D < 10−7 &LD êê Short@#, 3D &Abs@Last@%D − N@1ê GoldenRatioDD80.5, 0.666667, 0.6, 12 , 0.618034, 0.618034<2.55832×10−8
となる.Short[#,3]&がないと長々と出力されるので,途中の要素を省略して最初と最
後から3個の要素のみを表示した.最後に,誤差の対数も表示しておこう.
ListPlot@Log@Abs@FixedPointList@1êH1 + #L &, 0.5, 50,
SameTest → HAbs@#2 − #1D < 10−7 &LD − N@1êGoldenRatioDDD,PlotRange → 8−20, 0<, PlotStyle → [email protected],AxesLabel → 8"写像回数", "log»誤差»"<D;
2.5 5 7.5 1012.515写像回数
−20−17.5
−15−12.5
−10−7.5
−5−2.5
log»誤差»
最後に,Nestに関連したNestWhileListの使い方を述べておこう.基本的には
NestListと同じ使い方をする.具体例として,aをパラメータとする
xn = 1 + a logHxn-1Lを考えよう.初期値aを変数として,
iteratedLog@a_D :=
NestWhileList@1 + a Log@#D &, a, UnsameQ, All, 100Dと定義する.ここで,最大の反復回数を100として,終了条件を同じ値が2度続けて出るま
で繰り返すことを意味するUnsameQ(Allと合せて)とした.たとえば,
iteratedLog[3.]//Short[#,3]&,
iteratedLog[-2.0-2.0I]//Short[#,3]&
103
883., 4.29584, 5.37294, 42 , 6.71144, 6.71144<,8−2. − 2. , 36 , −8.45635 + 1.86415 <<のようになる.パラメータが-2.0 - 2.0 iの場合,初期値に依存する可能性のある最初の
20個を捨てて,実部と虚部を2次元で表示すると,
ListPlot@8Re@#D, Im@#D< & ê@ Drop@iteratedLog@−2.0 − 2.0 ID, 20D,PlotStyle → [email protected];
−8 −6 −4 −2 2
−10
−8
−6
−4
−2
2
と3個の点しか現れないので,周期3の変動になっていることが分かる.
この例の応用として,周期を求める関数を考えよう.今,データが8a, b, c, a, b, c<と与えられてるとする.反転して第2番目以降の要素を取り出すと,
Rest@Reverse@8a, b, c, a, b, c<DD8b, a, c, b, a<となる.最後の要素はcであるが,上記リストでcの位置はPositionを用いれば,
Position@8b, a, c, b, a<, _?H# c &L, 1D883<<となり,これから[[1,1]]]で取り出した値が周期になることが分かる.尚,最後の1は
レベルを表すが,デフォルトでは1はなくてもよい.
iteratedLogで作成したリストの長さが,反復回数H100L + 1ならば同じ値が2度続けて
現れないことになるので,周期解は存在しない.このような場合は単位リスト長1を返す
ことにすると,リストから周期を求める関数は,
myPeriod@list_D :=
If@Length@listD === 101, 1, Position@Rest@Reverse@listDD,_?H# Last@listD &L, 81<, 1D@@1, 1DDD
となる.いくつか例を示すと,
myPeriod@iteratedLog@#DD & ê@ 83.0, −2.0 − 2.0 I, 1.2 I<81, 3, 4<となる.以下のグラフには,パラメータをa 8cosHtL, sinHtL<として,aとtをいろいろ変
え,得られた周期の大きさに比例して表示する点の大きさを変えて表示すると,
104
Show@Graphics@Flatten@Table@8PointSize@myPeriod@iteratedLog@a 8Cos@tD + Sin@tD I<DDê
200.D, Point@a 8Cos@tD, Sin@tD<D<,8a, 1, 8, 0.25<, 8t, 0.0, N@2 πD, N@2 πDê15<D, 1D,PlotRange → All, Axes → True, AspectRatio → 1,
AxesLabel → 8"a cosHtL", "a sinHtL"<DD;
−7.5 −5 −2.5 2.5 5 7.5a cosHtL
−7.5
−5
−2.5
2.5
5
7.5
a sinHtL
となる.
最後に,sinを使ったいくつかのグラフを表示しておく.ここでは,合成関数を作成する
Compositionを使う.たとえば,
Composition@f, g, hD@xDf@g@h@xDDD
とすると分かるように,特別な場合として f = g = hとするとNestと同じ効果をもたら
す.このことから,CompositionはNestを拡張した関数になっていることが分かる.こ
こで,Tableを使って,
Table@Cos, 8j, 3<D@xD8Cos, Cos, Cos<@xDを作りだす.上記の例で, f = g = h = cosとして,さらにTableを使って,
Table@HComposition @@ Table@Cos, 8j, i<DL@xD, 8i, 3<D8Cos@xD, Cos@Cos@xDD, Cos@Cos@Cos@xDDD<
105
とすることで,sinにNestListに適用した効果と同じ式を得る.i = 1が最初の要
素,i = 2が次の要素を与える.事実,
NestList@Cos, Cos@xD, 2D8Cos@xD, Cos@Cos@xDD, Cos@Cos@Cos@xDDD<となる.以上のことから,下記に示す図が理解できるだろう.
Show@GraphicsArray@Block@8$DisplayFunction = Identity<,Plot@Evaluate@Table@HComposition @@ Table@#, 8j, i<DL@xD,8i, 10<DD, 8x, 0, 2 π<,
PlotStyle → Table@Hue@iê10.0D, 8i, 0, 9<D,PlotRange → All, PlotPoints → 40, Frame → True,
FrameLabel → 8"x", "Nested " <> ToString@#D<,PlotLabel → StyleForm@ToString@#D <> "の繰り返し"DD & ê@8Sin, Cos<DDD;
0 1 2 3 4 5 6x
−1−0.5
00.51
dets
eN
niS
Sinの繰り返し
0 1 2 3 4 5 6x
−1−0.5
00.51
dets
eN
soC
Cosの繰り返し
2.3.3 いくつかの応用
(1)パラメータに依存する方程式の解を求める
パラメータをもつ方程式の解も,基本的にはいろいろなパラメータ値に対して繰り返し
方程式を解けばよい.例として,λをパラメータとする
tanHxL = l xを考えよう.この場合,固定したλでも解は無数にある.l = 1とした場合,
Plot@Tan@xD − x, 8x, 0, 3 π<,AxesLabel → HStyleForm@TraditionalForm@#D,
FontFamily → "Times"D & ê@ 8x, Tan@xD<LD;2 4 6 8
x
−20
−10
10
tanHxL
106
から分かるように,x > 0の領域だけでも解は無限にある.x=4 .6近傍の解x*H1Lはstart = 81, x ê. FindRoot@Tan@xD == x, 8x, 4.6<D<81, 4.49341<
である.今,λを変えて解を求めるための関数を
transEq@λ_, x_D := Tan@xD − λ x
としよう.8l, x*HlL<を上記で求めたstartにすると,これから初めて,λをh間隔(デフォ
ルトでは-0.05とする)で,x=4 .6近傍の解を求め続ける.こうして,
contSols@f_, 8λn_, xn_<, h_ : −0.05D :=
Block@8a<, 8λn + h, a ê. FindRoot@f@λn + h, aD == 0, 8a, xn<D<Dと定義する.関数の中で,h_:-0.05としてデフォルトの設定を行う方法に注意した
い.今,h間隔としてデフォルト値を使用すると,l = 0.95での次の解は
contSols@transEq, startD80.95, 4.4817<と求まる.NestListで40回繰り返して解を求め続けて表示すると,
ListPlot@NestList@contSols@transEq, #1D &, start, 40D,PlotJoined → True, AxesLabel → 8"λ", "x∗HλL"<D;
−1 −0.5 0.5 1λ
2.5
3
3.5
4
4.5x∗HλL
となる.こうして,パラメータを変えたときに解を求めることができる.
(2)微分方程式の近似解法
微分方程式の解法に関しては後章で詳細に述べるが,ここでは,Nestを応用した常微
分方程式の逐次解法について述べる.今,2階微分方程式を
y '' HxL = - yHxLとして,初期条件yH0L = 1, y' H0L = 0のもとに解こう.まず,初期値を0次の近似解を
y0HxLとして, y0HxL = 1
と置く.これを微分方程式の右辺に代入すると1次の近似解y1HxLが得られる.そうすると,右辺は定数になるので,y1HxLは, y1HxL = 1 - Ÿ0
xd x ' Ÿ0
x'y0HxL Hx ''L d x ''
107
となる.この解を再び微分方程式の右辺に代入すれば,
y2HxL = 1 - Ÿ0xd x ' Ÿ0
x'y1Hx ''L d x ''
から2次の近似解y2HxLが決まる.以上の手順を繰り返して解が求められる.以上の操作をNestを用いた関数で記述しよう.必要なことは上記の右辺の積分なるので,
myApproximation@y_D := 1 − ‡0
x‡0
ξy x ξ
と定義する.ここで,Nestを適用し1次の近似解y1HxLを求めると,myApproximation@1D1 −
x2
2
となる.さらに5次まで近似を進める.結果は多項式になるので展開しておくと,
appro = Expand@Nest@myApproximation@#D &, 1, 5DD1 −
x2
2+
x4
24−
x6
720+
x8
40320−
x10
3628800
となる.ちなみに正確な解は,
y@xD ê.First@DSolve@8y''@xD −y@xD, y@0D 1, y'@0D 0<, y@xD, xDDCos@xD
である.そこで,cosHxLをx = 0の周りでxについて展開すると,
exact=Series[Cos[x],x,0,8]
1 −x2
2+
x4
24−
x6
720+
x8
40320+ O@xD9
となるので,誤差を求めると,
appro − exact
O@xD9となることが分かる.最後に,近似解が正解に近づいていく様子を見ると,
DisplayTogether@MapIndexed@8Plot@#1, 8x, 0, 2 π<, PlotRange → 8−1, 1<,
PlotStyle → 8Hue@#2@@1DDê7.D<,H∗MapIndexedの第2変数を使って近似解の色を変え∗LDisplayFunction → IdentityD< &,
Evaluate@Expand@NestList@myApproximation@#D &, 1, 7DDDD, H∗近似解の表示∗LPlot@Cos@xD, 8x, 0, 2 π<, PlotRange → 8−1, 1<DD;
108
1 2 3 4 5 6
−1−0.75
−0.5−0.25
0.250.50.75
1
となる.赤色の曲線が最も近似のよい5次の解である.尚,上記のコード
で,Plot[...]&は近似解を表示するための純関数を表している.
(3)文字列でネストを見る
ネストの作用は視覚的に捉えると分かりやすい.ButtonBoxはノートブックで使うボ
タンを表し,特殊文字Ãを使うと,
ButtonBox@myCHA = StyleForm@Ã, FontSize → 14DD êê DisplayFormÃ
のように表示される.これをTable形式で複数作ると,
Table@ButtonBox@myCHAD, 82<, 82<D êê MatrixForm êê DisplayFormikjjjjjjjjjjj à Ã
à Ã
yzzzzzzzzzzz
となる.このような表示をNestListを適用して,連続して作り出していくと,
NestList@GridBox@Table@ButtonBox@#D, 82<, 82<DD &,myCHA, 2D êê DisplayForm
9Ã, Ã Ã
à Ã,
à Ã
à Ã
à Ã
à Ã
à Ã
à Ã
à Ã
à Ã
=のようになる.
次の例も同じような作成方法である.まず,基本になる模様を上付き下付き文字ボック
スを表すSubsuperscriptBoxを用いて,
im = SubsuperscriptBox@myCHA, "M", "I"D êê DisplayFormÃM
I
109
と作成する.そして,NestListを適用すると,
NestList@SubsuperscriptBox@#, #, #D &, im, 2D êê DisplayForm9ÃM
I, ÃM
I
ÃM
IÃM
I
, ÃMI
ÃM
IÃM
I
ÃM
I
ÃM
IÃM
I
ÃM
I
ÃM
IÃM
I
=となり,フラクタル図形が作成できる(フラクタルに関しては後で述べる).
(4)扇を開く
円あるいは楕円を描くDiskを用いて作成した扇を,NestListを用いて徐々に開いてい
く様子を表してみよう.
Show@GraphicsArray@Graphics@#, AspectRatio → AutomaticD & ê@[email protected], Disk@80, 0<, 1, 8π ê2 − #, π ê2 + #<D< & ê@
NestList@# + 0.2 &, 0.5, 4DLDD;
【問題】上記のコ-ドで,NestListの働きを説明せよ.
(5)1 + a logHxLの周期解のカラー表示 今,差分方程式
xn = 1 + a logHxn-1Lの周期を求める関数をコンパイルを使って高速化しよう.関数は,
myCPeriod = Compile@88a, _Complex<<,Module@8listLog, i = 1, lastElement<,listLog = NestList@N@1. + a Log@#DD &, a, 100D;listLog = Reverse@listLogD;lastElement = listLog@@1DD;i = 2;
While@lastElement ≠ listLog@@iDD && i < 100, i = i + 1D;H∗逆にした最終要素lastElementと同じ要素を探索∗Li − 1DD;
であるが,最後の行にあるi - 1が周期を与える.そこで,複素数aをいろいろ変えて,周
期によってHueで彩色すると,
110
Timing@H∗計算時間∗LDensityPlot@myCPeriod@ar + ai D, 8ar, −3, 3<,8ai, −3, 3<, PlotPoints → 100, Mesh → False,
FrameLabel → 8"実部", "虚" <> "部"<, ColorFunction → HueDD
−3 −2 −1 0 1 2 3
実部
−3
−2
−1
0
1
2
3
虚部
82.922 Second, DensityGraphics <となる.
2.3.4 2次元Nest
これまで変数が1個の場合を考えていたが,2変数の場合に拡張すると,
xn = f Hxn-1, yn-1Lyn = gHxn-1, yn-1L
のような再帰方程式の解を求めることになる.最も簡単な場合は
xn = f Hxn-1Lyn = gHyn-1L
であるが,これは変数が1個の再帰方程式を2個並べただけで,変数間に相互作用はなく興
味ある現象は見らないであろう.純関数を使う場合,スコープ変数は#1=#であった
が,これが2つの要素であるリストであることに留意すると,#1[[1]]は
xnに,#1[[2]]はynに対応する.
たとえば,
xn = 1 + xn-1yn = cosHyn-1L
として,NestListで初期値8x0, y0< = 81, 0.5<から5回繰り返した値8x0, y0<~ 8x5, y5<を計算すると,
NestList@81 + #@@1DD, Cos@#@@2DDD< &, 81, 0.5<, 5D881, 0.5<, 82, 0.877583<, 83, 0.639012<,84, 0.802685<, 85, 0.694778<, 86, 0.768196<<となる.次の値は8x1, y1< = 81 + 1, cosH0.5L<である.NestListが適用する関数は純関数を
用いて表した.
111
次の例
xn = 1 + 0.9 xn-1yn = 4 yn-1H1 - yn-1L
は上記と同じような方程式であるが,座標を8xn, yn<としてグラフ化すると,ListPlot@NestList@81 + 0.9 #@@1DD, 4 #@@2DD H1 − #@@2DDL< &,80, 0.1<, 50D, PlotRange → All, PlotJoined → True,
AxesLabel → 8"xn", "yn"<, AspectRatio → 1D;
2 4 6 8 10xn
0.2
0.4
0.6
0.8
1yn
となる.xnは1に収束していく.
この例では変数間に相互作用はなく,xnとynは独立である.少し複雑にして,方程式を
xn = 1 + xn-1yn = xn-1 + yn-1
としよう.たとえば,初期値8x0, y0< = 80, 1<から5回繰り返すと,NestList@81 + #@@1DD, Plus @@ #< &, 80, 1<, 5D880, 1<, 81, 1<, 82, 2<, 83, 4<, 84, 7<, 85, 11<<
となる.第2式は,スコープ変数#2個の足し算なのでPlus@@#としている.
一般的には,まず関数を定義し,Nestを適用することが多いだろう.関数が
f2@x_, y_D := 9x2 + 1,è!!!!!!!!!!!y + 1 =
と与えられた場合について説明する.変数が2個あるので初期値も2個必要で,それらを8x0, y0<としよう.適用する関数は純関数で表すと,Nestを3回適用した結果8x3, y3<は,Nest@f2@#@@1DD, #@@2DDD &, 8x0, y0<, 3D91 + I1 + H1 + x02L2M2, $%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%1 +
"#########################1 + è!!!!!!!!!!!!!1 + y0 =
となる.また,NestListを適用すると,8x3, y3<までのすべての値を出力してNestList@f2@#@@1DD, #@@2DDD &, 8x0, y0<, 3D98x0, y0<, 91 + x02, è!!!!!!!!!!!!!1 + y0 =, 91 + H1 + x02L2, "#########################
1 + è!!!!!!!!!!!!!1 + y0 =,91 + I1 + H1 + x02L2M2, $%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%1 +"#########################1 + è!!!!!!!!!!!!!1 + y0 ==
112
となる.
以上の方法と異なり,特にスロット変数に関する異なる使い方を示す.変数が2個ある
場合,座標を8xn, yn<として図形を描く場合を考える.1次元より次元が増えたので複雑な
グラフィックスになることが予想される.具体例として,2次元の差分方程式を
xn+1 = yn - sign HxnL è!!!!!!!!!!!!!!!!!!!†b xn - c§yn+1 = a - xn
とする.ここで,a,b,cはパラメータである.純関数を用いると,方程式は
Apply@8#2 − Sign@#1D Sqrt@Abs@b #1 − cDD, a − #1< &, #D &I9#2 − Sign@#1D è!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Abs@b #1 − cD , a − #1= &M @@ #1 &
と表すことができる.この関数に値a, bを与えると,
Apply@8#2 − Sign@#1D Sqrt@Abs@b #1 − cDD, a − #1< &, #D &@8a, b<D9b − è!!!!!!!!!!!!!!!!!!!!!!!!!!!!Abs@a b − cD Sign@aD, 0=となる.ここで,スロット変数#の使い方に注意しよう.最右端にあるスロット変数#は8xn, yn<である.その#にApplyを適用するが,#には2つの要素,つまり第1変数#1と第
2変数#2があり,それぞれxn,ynに対応する.Nestを2回を適用すると,
Nest@Apply@8#2 − Sign@#1D Sqrt@Abs@b #1 − cDD, a − #1< &, #D &,8x0, y0<, 2D9a − x0 − "######################################################################################################AbsA−c + b Iy0 − è!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Abs@−c + b x0D Sign@x0DMESignAy0 − è!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Abs@−c + b x0D Sign@x0DE,
a − y0 + è!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!Abs@−c + b x0D Sign@x0D=となる.このようにして生成したデータを表示すると,NestListのグラフィカルな効用
が読み取れる.今,写像を10000回適用し点列8xn, yn<をPointで描く関数myComplexGを
myComplexG@8a_, b_, c_<, 8x0_, y0_<D :=
Show@Graphics@[email protected], Point ê@ NestList@Apply@8#2 − Sign@#1D Sqrt@Abs@b #1 − cDD, a − #1< &, #D &,8x0, y0<, 10000D<D, PlotRange → All,
AspectRatio → Automatic, DisplayFunction → IdentityDとして導入する.パラメータと初期値を適当に設定すると,フラクタル的構造をもつグラ
フィックス
Show@GraphicsArray@ReleaseHold@myComplexG@#D & ê@8Hold@[email protected], 1.0, 1.0<, 80.0, 0.1<DD,Hold@[email protected], 1.0, 0.5<, 80.2, 0.3<DD<DDD;
113
が得られる.パラメータと初期値をいろいろ変えて描いてほしい.
【問題】上記のプログラムで,ReleaseHoldとHoldを使って,2種類のパラメータを設
定して計算している.なぜ,このようなコマンドが必要なのか解析せよ.
もう少しカラー表示の表現に凝ってみよう.今,MapIndexedは
MapIndexed@8Hue@#2@@1DDD, Point@#1D< &,
NestList@g@#D &, a, 2DD88Hue@1D, Point@aD<,8Hue@2D, Point@g@aDD<, 8Hue@3D, Point@g@g@aDDD<<と作用する.インデックスされた変数をPointのカラー表示に使うと,
myComplexCG@8a_, b_, c_<, 8x0_, y0_<, nPoints_D :=
Graphics@[email protected], MapIndexed@8Hue@#2@@1DDê nPointsD, Point@#1D< &, NestList@Apply@8#2 − Sign@#1D Sqrt@Abs@b #1 − cDD, a − #1< &, #D &,8x0, y0<, nPointsDD<,
PlotRange → All, AspectRatio → AutomaticD;と変更できる.いくつかパラメータに対して表示すると,
Show@GraphicsArray@myComplexCG@#, 80.0, 0.1<, 10000D & ê@
Table@N@8Cos@tD, Sin@tD, 0.1<D, 8t, π, 2 π, π ê4<DDD;
となる.
標準写像と呼ばれる再帰方程式に応用しよう.写像は
114
xn = xn-1 + yn-1HmodL 2 pyn = yn-1 + k sinHxn-1 + yn-1L HmodL 2 p
と記述される.この写像は,
standardMap1@k_, x_, y_D := 8x + y, y + k Sin@x + yD<において2 pで割った余りなので,x,y両方向に2 pの周期をもつ.この写像の特徴は,各
変数の微小変化が
ikjjj d xnd yn
yzzz =
ikjjjjjjjjjjj ∑ xnÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ∑ xn-1
∑ xnÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ∑ yn-1∑ ynÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ∑ xn-1
∑ ynÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅ∑ yn-1
yzzzzzzzzzzz ikjjj d xn-1
d yn-1
yzzzと記述できるが,ここに現れる行列式が常に
Det@88∂x #@@1DD, ∂y #@@1DD<, 8∂x #@@2DD, ∂y #@@2DD<< &@standardMap1@k, x, yDDD
1
となるので保存系の写像と呼ばれている.さて,2 pで割った余りで表現すると,
standardMap@k_, x_, y_D :=8Mod@x + y, 2 πD, Mod@y + k Sin@x + yD, 2 πD<のように記述できる.表示のために,
standardEq = Graphics@Text@DisplayForm@FrameBox@
FormBox@StyleBox@MakeBoxes@"Satndard Map Hmod 2πL\n xn=
xn−1+yn−1\n yn=yn−1+1.2sinHxn−1+yn+1L",TraditionalFormD, FontFamily → "Times",
FontSize → 10D, TraditionalFormDDD,[email protected], 0.13<D, Background → [email protected];
を準備しておく.そして,500回繰り返し,最初の100回を捨てた残りで最終的なグラフィ
ックスを作成すると,
DisplayTogether@8Graphics@8Hue@Random@DD, [email protected],Point ê@ Drop@NestList@[email protected],
#@@1DD, #@@2DDD &, #, 500D, 100D< & ê@
Table@Random@Real, 80, 2 π<D, 8100<, 82<D,PlotRange → 880, 2 π<, 80, 2 π<<, Frame → True,
FrameTicks → 880, π ê 2, π, 3 π ê2, 2 π<,80, π ê2, π, 3 π ê2, 2 π<, None, None<,FrameStyle → [email protected], [email protected]<,FrameLabel → 8"x", "y"<, TextStyle →8FontFamily → "Times", FontSize → 12<, AspectRatio → 1D,standardEq<D;
115
pÅÅÅÅÅ2
p 3 pÅÅÅÅÅÅÅÅÅÅ2
2 p
x
pÅÅÅÅÅ2
p
3 pÅÅÅÅÅÅÅÅÅÅ2
2 py
Satndard Map Hmod 2pLxn=xn-1+yn-1
yn=yn-1+1.2sinHxn-1+yn+1Lとなる.この図で,空白になっている楕円形の部分は,そこには点が来ないことを示
す.また,そのような部分の中に縮小した楕円があり,フラクタル構造になっていること
も確認できる.
2.3.5 FoldとFoldListの利用
Fold,FoldListもNestと同様に,繰り返し演算に使われる.グラフィックスへの応
用例を用いながら説明する.3次元ベクトルn,pを与えた時,それらのベクトルに直交す
る新たな直交座標を計算する関数を考える.具体例として,
n = 81, 0, 0<; p = 80, 1, 0<;と与えると,Crossでnとpの外積
newn = Cross@n, pD80, 0, 1<を計算する.外積の定義からnewnは,n,pに直交する.実際,newnとn,newnとpの内
積を計算すると,8newn.n, newn.p<
116
80, 0<と両者とも0になる.このように求めたnewnとpに直交する新たなベクトルは,再び外積
を用いて,
Cross@newn, pD8−1, 0, 0<と求まる.これは,newnとpに直交する.つまり,newnとCross[newn,p]は新たな直交
座標系になる(pの代わりにnを用いることもできる).以上の直交関係を図示するため
に,新たに2個のベクトルをまとめて,
gg = 880.5, 0.5, 0.5< + 0.5 #, #< &@Cos@π ê4.D p + Sin@π ê4.D Cross@newn, pDD880.146447, 0.853553, 0.5<, 8−0.707107, 0.707107, 0<<
と導入する.ggの2番目のベクトルは,pとCross[newn,p]で張られた平面で角度π/4回
転させたベクトルである.ggの最初のベクトルは0.5,0.5,0.5+0.5 #と平行移動し
ている.3次元表示すると,
Show@Graphics3D@[email protected], Line@880, 0, 0<, n<D<,Text@"n", n + 80, 0, 0.1<D, H∗ベクトルn,黒∗[email protected], Line@880, 0, 0<, p<D<,Text@"p", p + 80, 0, 0.1<D, H∗ベクトルp,黒∗[email protected], Line@880, 0, 0<, newn<D<,Text@"newn", newn + 80, 0, 0.04<D, H∗ベクトルnew,黒∗[email protected], Line@880, 0, 0<, Cross@newn, pD<D<,H∗ベクトルCross@newn,pD,黒∗LText@"Cross@newn,pD", Cross@newn, pD + 80.4, 0, 0.3<D,[email protected], [email protected], Line@880, 0, 0<, #<D & ê@ gg<<,H∗ベクトルgg,カラ−∗LAxes → True, AspectRatio → 1DD;
−1−0.5
0
0.5
10
0.250.50.7510
0.25
0.5
0.75
1
n
p
newn
ross@newn,pD1
−0.50
0.5
0
117
と表せる.図で表すと,n,p,Cross[newn,p]の位置関係が読み取りやすい.
以上のことを応用して,さらに複雑な図形を表示しよう.今,
Line@880, 0, 0<, #<D & ê@ # & ê@
Table@88Cos@tD, Sin@tD, 0<, 80, 0, 1<, 8Cos@tD, Sin@tD, 0<<,8t, 0, 2 π, 2 π ê1<D êê N88Line@880., 0., 0.<, 81., 0., 0.<<D,Line@880., 0., 0.<, 80., 0., 1.<<D,Line@880., 0., 0.<, 81., 0., 0.<<D<,8Line@880., 0., 0.<, 81., 0., 0.<<D,Line@880., 0., 0.<, 80., 0., 1.<<D,Line@880., 0., 0.<, 81., 0., 0.<<D<<
とすれば,原点から鉛直上方と,原点からz = 0で表される平面上で半径1の円周上の点を
結ぶ線分を表す(同じ線が2回引かれている).表示すると,
Show@Graphics3D@Line@880, 0, 0<, #<D & ê@ # & ê@ Table@88Cos@tD, Sin@tD, 0<, 80, 0, 1<, 8Cos@tD, Sin@tD, 0<<,8t, 0, 2 π, 2 π ê50<D, Axes → TrueDD;
−1−0.5
00.5
1−1
−0.5
0
0.5
1
00.250.5
0.751
1−0.5
00.5
のように,z = 0平面上では放射状に伸びる線分になる.さて,ggを参考にして,
step@8mp_, n_, p_<, r_D :=
Module@8newn = Cross@n, pD, b<,b = Cross@newn, pD; Table@8mp + r #, newn, #< &@Cos@tD p + Sin@tD bD, 8t, 0, 2 π, 2 π ê6.<DD;
と定義しよう.FoldListの適用する初期値を原点にとると,
118
Show@Graphics3D@MapIndexed@[email protected]ê#2@@1DDD,
Hue@#2@@1DDê3D, Line@First ê@ #1D< &,FoldList@Function@8x, y<, Map@step@#, yD &, x, 8−3<DD,Table@88Cos@tD, Sin@tD, 0<, 80, 0, 1<,8Cos@tD, Sin@tD, 0<<, 8t, 0, 2 π, 2 π ê5.<D,81ê 2, 1ê4, 1ê 12<H∗異なる3個の放射∗LD, 8−4<D,
Boxed → FalseDD;
となる.繰り返し演算につきものの複雑な図形になる.このグラフィックスを理解上での
ポイントはMapIndexedで,一般に,
MapIndexed@8#1, #2< &, 8a, b, c<D88a, 81<<, 8b, 82<<, 8c, 83<<<となることである.インデックス#2[[1]]は線の太さや彩色に使っている.
【問題】MapIndexedの使い方を参考に,上記のグラフィクスの動作を解析せよ.
次に,
$%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%r −"######################r −
è!!!!!!!!!!!r −∫
で表される系列を考えよう.ここではrは0あるいは1である.各繰り返しでの上記の値は
一般に複素数になる.たとえば,
FoldList@H#1 − #2L1ê2 &, 1.0,
Table@Random@Integer, 80, 1<D, 85<DD81., 0., 0. + 1. , 0.707107 + 0.707107 ,
0.92388 + 0.382683 , 0.980785 + 0.19509 <である.横軸に実部,縦軸に虚部をとって図示すると,
Show@Table@Graphics@[email protected],Point@8Re@#D, Im@#D<D & ê@ FoldList@H#1 − #2L1ê2 &,1.0, Table@Random@Integer, 80, 1<D, 810000<DD<DDD;
119
のようになる.これは個の説明する典型的なフラクタル図形で,CGなどに良く用いれて
いる.
2.4 フラクタル図形
マンデルブローによって1983年に導入されたフラクタルの特徴は,入れ子構造になって
いることである.既に何度か示してきたが,フラクタル作成にはNestが重要な役割を果
たす.ここで,フラクタル図形とその意味することについて検討する.
2.4.1 ネストで作る図形
これまで見てきたように繰り返し演算にはNest,NestList等がたいへん有効で,また
それらはそのような使い方に適した組込み関数であった.ここでは,Nestがグラフィッ
クスで効果的に適用できる例をいくつか述べた後,フラクタル図形へと発展させる.まず
簡単な例から始めよう.
今,正方形のグラフィックスプリミティブ
myRec = [email protected], Rectangle@8−1, −1<, 81, 1<D<;を用意する.次に,NestListを作用させる関数を
fRec@a_D@8Hue@c_D, Rectangle@8x1_, y1_<, 8x2_, y2_<D<D :=8Hue@a cD, Rectangle@a 8x1, y1<, a 8x2, y2<D<と定義する.これは,正方形の左下の座標を与えるリスト8x1, y1<と右上の座標を与えるリスト8x2, y2<をそれぞれa倍にし,色もHue[c]からHue[a c]に変える.この関数をaが異
なる3つの正方形に10回作用させて,同時に表示すると,
Show@GraphicsArray@8Graphics@NestList@fRec@#D, myRec, 10D,PlotLabel → "大きさ=" <> ToString@#D,AspectRatio → AutomaticD & ê@ 80.75, 0.5, 0.25<<DD;
120
大きさ=0.75 大きさ=0.5 大きさ=0.25
となる.ここで用いたMap(/@)の作用に関して説明する必要はないだろう.Nest-
Listの作用は,fRecを作用させて変化している様子から理解できる.
次に,回転を取り入れた幾何学的な要素を付け加えてみよう.まず回転行列が使えるよ
うに,標準パケージ<<Geometry`Rotations`をロードする.塗り潰さない1辺の長さ
2の正方形プリミティブLineを用いて
mySqu = Line@88−1, −1<, 81, −1<, 81, 1<, 8−1, 1<, 8−1, −1<<D;と作成する.閉じた正方形を作るために,最初の要素8-1, -1<を最後の要素に付け加える必要がある.正方形の大きさaと,回転角度qの変数を2個持つ関数
fRot@a_, θ_D@Line@88x1_, y1_<, 8x2_, y2_<,8x3_, y3_<, 8x4_, y4_<, 8x1_, y1_<<DD :=
Line@a HRotationMatrix2D@θD.#L & ê@88x1, y1<, 8x2, y2<, 8x3, y3<, 8x4, y4<, 8x1, y1<<Dを定義する.回転角度はp ê 24とし,正方形の大きさを変えて表示しよう.関数fRotを正
方形に24回作用して表示すると,
Show@GraphicsArray@Graphics@NestList@fRot@#, π ê24.D, mySqu, 24D, AspectRatio →
AutomaticD & ê@ 80.6, 0.8, 1 − π ê24, 1, 1.5<DD;となる(p ê 24.として実数化にしておくこと).これらの図は上に定義した関数だけから
生成されたとは想像し難い.これがNestで作成したグラフィックスの顕著な特徴で,複
雑に見えるフラクタル図形であってもその生成方法が実に簡単である.
さらに,入れ子構造になっている複雑なグラフィックスに挑戦しよう.まず,塗り潰さ
ない一辺長さ2の正方形をLineを用いて
mySqu1 = Line@880, 0<, 80, 1<, 81, 1<, 81, 0<, 80, 0<<D;とする(mySquとは位置が異なる).次に,横軸に平行に8x, y<から8x + d h, y<に線を引き,また,縦軸に平行に8x, y<から8x, y + d v<に線を引くための関数
121
lh@8x_, y_<, dh_D := Line@88x, y<, 8x + dh, y<<Dlv@8x_, y_<, dv_D := Line@88x, y<, 8x, y + dv<<D
を用意する.上記の各関数で用いているLineに対して作用させる,
myBer@Line@8a_, b_<DD := Line@8H1 + aLê2, H1 + bLê 2<Dとする関数myBerを導入する.ここで,myBerの変数a_,b_は,lhおよびlvでの変数8x, y<に対応している.たとえば,
NestList@myBer, lh@8a, b<, dD, 1D9Line@88a, b<, 8a + d, b<<D,LineA99 1 + a
2,
1 + b2
=, 9 12H1 + a + dL, 1 + b
2==E=
とするとmyBerの作用が分かる.さて,NestListをk回繰り返して適用するため
に,myBerをlhおよびlvに作用させる関数
squareNest@k_D := Graphics@8mySqu1,NestList@myBer, lh@81, 0.5<, −1D, kD, H∗横方向のネスト∗LNestList@myBer, [email protected], 0.5<, 0.5D, kDH∗縦方向のネスト∗L<, AspectRatio → AutomaticD
を定義する.ここで,図形の枠を描くための正方形mySqy1も加えた.lhおよびlvで設定
した変数値の意味は,以下に示す図を見れば理解できるだろう.
Show@GraphicsArray@Partition@Table@squareNest@kD, 8k, 0, 5<D, 3DDD;
正方形が徐々にネストされ小さくなっていく様子が見える.尚,Partitionで3個ずつ図
を表示するようにした.
【問題】関数squareNestで,lhおよびlvに与える変数の値をいろいろ変えた図を描い
てみよう.たとえば,lh[1,0.5,-1],lv[0.5,0.5,0.5]としてみよう.
122
もう少し進めよう.今,大きさ1の正方形の横と縦をそれぞれ8a, b<を基準にして,delta間隔でn個の格子に分ける関数を,
ggh@delta_, n_D@Line@88x1_, y1_<, 8x2_, y2_<<DD :=
Line@88x1, y1 + deltaên<, 8x2, y2 + deltaên<<Dggv@delta_, n_D@Line@88x1_, y1_<, 8x2_, y2_<<DD :=
Line@88x1 + deltaê n, y1<, 8x2 + deltaên, y2<<Dと定義する.これらの関数は,delta間隔で横軸,縦軸に平行線を引くためのものであ
る.
【問題】関数ggh,ggvを,NestListに使うことを前提に,もう少しすっきりした関数
に修正してみよう.
例として,正方形mySqy1に中にn = 5とした格子を描くために.まず,
NestList@ggh@1, 5D, lh@8a, b<, dD, 1D9Line@88a, b<, 8a + d, b<<D, LineA99a, 15
+ b=, 9a + d,15
+ b==E=となることを考慮する.して,正方形mySqy1に中にn = 5とした格子を描くと,
Show@Graphics@8mySqu1,NestList@ggh@1, 5D, lh@80, 0<, 1D, 5D, H∗横方向のネスト∗LNestList@ggv@1, 5D, lv@80, 0<, 1D, 5DH∗縦方向のネスト∗L<, Axes → TrueD, AspectRatio → AutomaticD;
0.2 0.4 0.6 0.8 1
0.2
0.4
0.6
0.8
1
となる.分かり易いようにAxes→Trueとして座標を表示した.このように作成した格子
をGrayLevelで表示しよう.関数latticeを
lattice@8a_, b_<, delta_, n_, m_, r_: 0D :=
Flatten@8GrayLevel@rD, H∗グレ−スケ−ル表示∗LNestList@ggh@delta, nD, lh@8a, b<, deltaD, mD, H∗横軸を描く∗LNestList@ggv@delta, nD,lv@8a, b<, deltaD, mDH∗縦軸を描く∗L<D
と定義する.ここで,Flattenの必要性を確認しておこう.すると,
123
Show@Graphics@[email protected], 0.4<, 0.2, 6, 5, 0.6D,H∗格子 80.2,0.4< の中に小さな格子を描く∗[email protected], 0.2<, 0.2, 6, 5, 0.2D,H∗格子 80.6,0.2< の中の小さな格子を描く∗Llattice@80, 0<, 1, 5, 5, 0DH∗大きな格子∗L<, Axes → TrueD, AspectRatio → AutomaticD;
0.2 0.4 0.6 0.8 1
0.2
0.4
0.6
0.8
1
のように格子が入れ子になる.さて,このようなlatticeを多数作る関数を
myFinal@n_, opts___RuleD :=
TableA9latticeA9 nk − 1
nk− u, u=, 1
nk, n, n, 1 − Hk + 1L−1E=,8k, n − 1, 0, −1<, 9u, 0,
nk − 1
nk,
1
nk=E;
と定義する.どのような幾何学模様ができるかは,
Show@GraphicsArray@Graphics@#, AspectRatio → AutomaticD & ê@8myFinal@2D, myFinal@3D, myFinal@4D<DD;
とすれば分かる.このような入れ子構造になる図形は,典型的なフラクタル図形であ
る.ただし,nが6程度の大きさでも,メモリの消費が激しく,カーネルがストップするの
で注意しよう.この段階でのMathematicaが利用できるメモリは
MaxMemoryUsed@D183413744
124
バイトであった(この値と以下にも示す値は,コンピュータに搭載されているメモリ,ま
た使用状況によって異なる).さらに,myFinal[6]を実行するのに要した消費メモリを
求めると,
startMem = MemoryInUse@D;myFinal@6D;MemoryInUse@D − startMem
32224584
となり,かなり消費していることが分かる.当然計算時間もかかる.
もう一つ例を示そう.まず,
Show@Graphics@Line ê@
Flatten@NestList@#ê2 &, 8881, 1<, 83, 1<, 81, 3<, 81, 1<<,881, 0<, 82, −1<, 82, 1<, 81, 0<<<, 2D, 1D,Axes → True, AspectRatio → 1, PlotRange → AllDD;
0.5 1.5 2 2.5 3
−1
1
2
3
となる.図形の作成法を確認しよう.このようなネスト図形を作成する関数を
nestedTriangles@n_Integer?PositiveD :=HFunction@8x, y<, x.# & ê@ yD @@ #L & ê@
Distribute@8Table@88Cos@i π ê2D, Sin@i π ê2D<,8−Sin@i π ê2D, Cos@i π ê2D<<, 8i, 0, 3<D,Flatten@NestList@#ê2 &, 8881, 1<, 83, 1<, 81, 3<, 81, 1<<,881, 0<, 82, −1<, 82, 1<, 81, 0<<<, nD, 1D<, ListD
とする.
【問題】関数nestedTrianglesがどのように動作するか,解析せよ.
上記の関数を用いて作成したフラクタル図形の一例に
Show@Graphics@Line ê@ nestedTriangles@3D,AspectRatio → AutomaticDD;
125
がある.
【問題】Plot[Evaluate[NestList[Sin[#+x]]&,0,50],x,0,2π,Frame→
True,PlotRange→All,PlotStyle→Table[Hue[i/50],i,50]]がどのような図を
描くか調べよう.
最後に,円を使ったフラクタル図形を描こう.今,
twoNewCircles@Circle@8x0_, y0_<, r_, 80, π<DD :=8Circle@8x0 − r, y0 − rê 2<, rê2, 80, π<D,Circle@8x0 + r, y0 − rê 2<, rê2, 80, π<D<H∗与えられた半円を用いて,rê2 下に左右に二つの半円を描く∗L
twoNewCircles@Circle@8x0_, y0_<, r_, 8π, 2 π<DD :=8Circle@8x0 − r, y0 + rê 2<, rê2, 8π, 2 π<D,Circle@8x0 + r, y0 + rê 2<, rê2, 8π, 2 π<D<H∗与えられた半円を用いて,rê2 上に左右に二つの半円を描く∗L
と定義する.同じ名前の関数であることに注意.そこで,表示すると,
Show@Graphics@[email protected],NestList@Flatten@twoNewCircles ê@ #D &, 8#<, 6D & ê@8Circle@80, 1<, 1, 80, π<D,Circle@80, −1<, 1, 8π, 2 π<D<<D,
AspectRatio → Automatic, PlotRange → AllD;
126
のようになる.
2.4.2 タイプセット,組込み関数で作るフラクタル
フラクタル図形の特徴である入れ子構造を視覚的に表すにはグラフィックスを用いるの
が効果的であるが,それ以外の方法でも入れ子構造は表せる.Mathematicaには各種のタイ
プセットが用意されている.添え字などの特徴をうまく使えば,タイプセットだけでもフ
ラクタル図形は簡単に作成できる.フラクタルに特有な入れ子構造は,Subsuper-
scriptBoxを使うと作成できる.たとえば,
SubsuperscriptBox@"Ω", "Ω", "Ω"D êê DisplayFormΩΩ
Ω
とすれば,文字の右側の上下に同じ文字を添え字として付け加えることができるので,こ
れを入れ子構造を作成する基本単位と考えるればよい.そこで,Wから初めて,Nestを適
用すると,
Nest@SubsuperscriptBox@#, #, #D &, "Ω", 4D êê DisplayFormΩΩ
ΩΩΩ
ΩΩΩ
Ω
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
ΩΩΩ
のように,全体のサイズが大きくなるが,入れ子構造が構築されいることが分かる.この
図形はシェルピンスキーと呼ばれているフラクタル図形である.
以下ではもう一つタイプセットを用いた,少し異なるシェルピンスキーフラクタルの例
を示す.GridBoxを用いて,フラクタル図形を作成する.たとえば,
GridBox@88" ", " ", " "<, 8" ", " ", " "<, 8" ", " ", " "<<,RowSpacings → 0.1, ColumnSpacings → 0,
RowMinHeight → 0D êê DisplayFormとすれば,中央が空白の四角形ができる.StyleBoxを使えば,先の例と同様にフラクタ
ル図形
StyleBox@Nest@GridBox@88#, #, #<, 8#, " ", #<, 8#, #, #<<D &, " ", 3D,FontSize → 6, GridBoxOptions → 8RowSpacings → 0.1,
ColumnSpacings → 0, RowMinHeight → 0<D êê DisplayForm
127
ができる.この図形もシェルピンスキーフラクタルで入れ子構造がよく見える.
標準組込み関数にIFSと呼ばれる関数がある.これを使えば一般的なフラクタル図形が
描ける.<<ProgrammingInMathematica`IFS`をロードしておく.例として,最初
に,
gr0 = Show@Graphics@[email protected], [email protected], .0<, 80.5, 0.5<D<,[email protected], [email protected], .5<, 80.5, 1.0<D<,[email protected], [email protected], .5<, 81.0, 1.0<D<<, AspectRatio → AutomaticDD;
のような図形を用意する.AffineMap[φ,ψ,r,s,e,f]は元の図形を
ikjjj r 00 s
yzzz ikjjj cos f sin j
-sin j cos fyzzz ikjjj x
yyzzz +
ikjjj efyzzz
のようにアフィン変換する.ここで,8e, f <は平行移動量,8f, j<は回転角度を表す.そこで,アフィン変換を施す関数を,Hifs0 = IFS@8 [email protected], 0.5D, [email protected], 0.0, 0.5, 0.5, 0.5, 0.5D,
[email protected], 0.0, 0.5, 0.5, 0.0, 0.5D<DL êê FullFormifs@List@map@List@[email protected]`, 0, 0D, List@0, 0.5`, 0DDD,map@List@[email protected]`, 0.`, 0.5`D, List@0.`, 0.5`, 0.5`DDD,map@List@[email protected]`, 0.`, 0.`D, List@0.`, 0.5`, 0.5`DDDD,
List@Rule@Probabilities, AutomaticDDD
128
と定義しておく.gr0を初期の図形として,3回まで反復してフラクタル図形をプロット
すると,
Show@GraphicsArray@NestList@ifs0, gr0, 4DDD;
となる.
2.4.3 シェルピンスキーフラクタル
タイプセットだけでも,実に簡単にシェルピンスキーのフラクタル図形を作成すること
できた.ここでは,一般的なフラクタル図形が作成できるように,フラクタル図形の基本
構造である入れ子構造をグラフィックスとして記述する.シェルピンスキーフラクタル
は,後で述べるカントール集合を2次元に拡張した図形である.各ステップで1 ê 2で相似的
に縮小した図形3個で,次のステップの図形を覆うことができる.したがって,フラクタ
ル次元は
Log@3Dê Log@1êH1 ê2LD êê N1.58496
と求まる(後で詳細に述べる).直感的には,線(1次元)と面積(2次元)の間で,空間
を疎に充填した図形の非整数な次元である.
以下では,三角形を基礎にしたシェルピンスキー図形を作成する.最初の三角形の1辺
は長さを2として,
ShowAGraphicsAISierpinskiTriangle@1D =9PolygonA98−1, 0<, 81, 0<, 90, è!!!!!!3. ==E=M, Axes → TrueEE;
−1 −0.5 0.5 1
0.250.50.75
11.251.51.75
とする(1辺は長さは任意である).最初の三角形から次の三角形を構成する方法は,最
初の三角形を相似的1 ê 2に縮小して,小三角形を3個作成すればよい.今,最初の三角形の
3頂点をベクトル8a, b, c<とすると,左下に置く小さな三角形の頂点は8a, Ha + bL ê 2, Ha + cL ê 2<と表せる.元の三角形SierpinskiTriangle[1]の各頂点の座標
129
を,これらの座標で置き換えれば小三角形の座標が確定できる.このようにして3個の三
角形の頂点の座標を決め,Polygonで小三角形のプリミティブを作成すると,
SierpinskiTriangle@1D ê.Polygon@8a_, b_, c_<D → 8Polygon@8a, Ha + bLê 2, Ha + cLê2<D,Polygon@8Ha + bLê2, b, Hb + cLê 2<D,Polygon@8Ha + cLê2, Hb + cLê 2, c<D<99PolygonA98−1, 0<, 80, 0<, 9−
12, 0.866025==E,
PolygonA980, 0<, 81, 0<, 9 12, 0.866025==E,
PolygonA99−12, 0.866025=, 9 1
2, 0.866025=, 80, 1.73205<=E==
となる.このコードは,最初の三角形から小三角形を作る1ステップの動作を表している
ので,任意のnステップでは小三角形SierpinskiTriangle[n]はSierpinski-
Triangle[n-1]から,
SierpinskiTriangle@n_IntegerD :=
SierpinskiTriangle@nD = N@Flatten@SierpinskiTriangle@n − 1D ê. 8Polygon@8a_, b_, c_<D →8Polygon@8a, Ha + bLê 2, Ha + cLê2<D,
Polygon@8Ha + bLê2, b, Hb + cLê2<D,Polygon@8Ha + cLê2, Hb + cLê 2, c<D<<DD
とすれば作成できる.入れ子構造の関数的な特徴は,この例からも分かるように関数の定
義にパラメータ(n)を変更した関数自体を使っていることである.このような再帰的な
構造をもつ関数がフラクタルの入れ子構造を特徴付ける.n = 5までのシェルピンスキー図
形は,
Show@GraphicsArray@Graphics@SierpinskiTriangle@#D,
PlotLabel → StyleForm@"Sierpinski n=" <> ToString@#D,"Times", FontSize → 8D,
AspectRatio → AutomaticD & ê@ 81, 2, 4, 5<DD;Sierpinski n=1 Sierpinski n=2 Sierpinski n=4 Sierpinski n=5
となる.
以下では,多彩なシェルピンスキー図形を作ってみよう.最初の例は各三角形を異なる
色で彩色して
130
Show@Table@Graphics@8Hue@1 − nê6D, SierpinskiTriangle@nD<D,8n, 6<D, AspectRatio → AutomaticD;
とした.ここで,最初の大きな三角形が最も後ろで,最後のn = 6での小三角形が最も手前
になっていることに留意しよう.次に,多角形の辺を表す線を別の曲線で置き換えてみよ
う.このためには,Polygon[list]の変数であるリストlistを入れ替えればよい.ま
ず,3頂点をベクトル8a, b, c<を,Partition@Append@8a, b, c<, First@8a, b, c<DD, 2, 1D88a, b<, 8b, c<, 8c, a<<
として,3辺を表すベクトルを構成する.そこで,辺8a, b<に対して,With@8n = 4<,Join @@ HTable@#@@1DD + kê n H#@@2DD − #@@1DDL, 8k, 0, n<D & ê@88a, b<<LD9a, a +
14H−a + bL, a +
12H−a + bL, a +
34H−a + bL, b=
として,8a, b<の中に新たに4点を追加する.新たな点を追加する関数を
addPoints@Polygon@list_D, n_D := Polygon@Join @@ HTable@#@@1DD + kên H#@@2DD − #@@1DDL, 8k, 0, n<D & ê@
Partition@Append@list, First@listDD, 2, 1DLDとして導入する.ここで,
#ê #.# & ê@ HH# − 80, [email protected]ê 3<L & ê@ 88a, a1<<L99 a
a2 + H−0.57735 + a1L2 , −0.57735 + a1
a2 + H−0.57735 + a1L2 ==と変換すると,
ShowATableAGraphicsA9Hue@Sin@nDD, SierpinskiTriangle@nD ê.p_Polygon addPoints@p, 10 − nD ê. Polygon@list_DPolygonA #
#.#& ê@ HH# − 80, [email protected]ê 3<L & ê@ listLE=E,8n, 6<E, AspectRatio → AutomaticE;
131
のような図形ができる.
【問題】上記のコ-ドで,いろいろパラメ-タを変えて,どのようなグラフィックスになる
か試みよう.
同じシェルピンスキー図形であるが,別なアプローチで作成しよう.今,
z1 = 881, 1, 1<, 81, 1, 0<, 81, 0, 0<<;z2 = 880, 0, 0<, 80, 0, 0<, 80, 0, 0<<;
とする.特に前者のリストは,
ListDensityPlot@−z1, Mesh → False, FrameTicks → FalseD;
のようになるが,これまで述べたきた三角形と異なるが,この図形をフラクタル図形を作
り出す最初の図形とする.今,z1から初めてネストを適用する関数を
mS@iterations_D :=
mS@iterationsD = Nest@Flatten@MapThread@Join, 8##<D & @@@H# ê. 81 → z1, 0 → z2<L, 1D &, z1, iterations − 1Dと定義しよう.フラクタル図形たらしめる操作は,ネスト構造を作り出す置換,#/.1→
z1,0→z2である.関数の意味を説明しておく.まず,置換
1 → z1
1 → 881, 1, 1<, 81, 1, 0<, 81, 0, 0<<は,1である要素をz1そのもので置き換える作用である.同様に,
0 → z2
0 → 880, 0, 0<, 80, 0, 0<, 80, 0, 0<<
132
は,0である要素をz2そのもので置き換える作用である.したがって,関数H# &@z1DL êê TableFormH# ê. 81 → z1, 0 → z2< &@z1DL êê TableForm1 1 11 1 01 0 0
1 1 11 1 01 0 0
1 1 11 1 01 0 0
1 1 11 1 01 0 0
1 1 11 1 01 0 0
1 1 11 1 01 0 0
0 0 00 0 00 0 0
1 1 11 1 01 0 0
0 0 00 0 00 0 0
0 0 00 0 00 0 0
において,#はz1なので上記の結果が理解できるだろう.最初の文は,z1のテーブルを表
しているが,z1の要素である1,0をそれぞれz1,z2で置き換えていて作成したテーブル
になっている.行列としては拡大されているが,元の図形であるz1をz1,z2を使って入
れ子にしている.尚,このこのままでは,ListDensityPlotで図示できない.そこで,
MapThread@Join, 8##<D & @@@888a1, b1, c1<, 8a2, b2, c2<, 8a3, b3, c3<<<88Join@a1, a2, a3D, Join@b1, b2, b3D, Join@c1, c2, c3D<<となることを考慮し,
MapThread@Join, 8##<D & @@@ H# ê. 81 → z1, 0 → z2< &@z1DL8881, 1, 1, 1, 1, 1, 1, 1, 1<,81, 1, 0, 1, 1, 0, 1, 1, 0<, 81, 0, 0, 1, 0, 0, 1, 0, 0<<,881, 1, 1, 1, 1, 1, 0, 0, 0<, 81, 1, 0, 1, 1, 0, 0, 0, 0<,81, 0, 0, 1, 0, 0, 0, 0, 0<<, 881, 1, 1, 0, 0, 0, 0, 0, 0<,81, 1, 0, 0, 0, 0, 0, 0, 0<, 81, 0, 0, 0, 0, 0, 0, 0, 0<<<する.つまり,
Nest@Flatten@MapThread@Join, 8##<D & @@@H# ê. 81 → z1, 0 → z2<L, 1D &, z1, 1D881, 1, 1, 1, 1, 1, 1, 1, 1<,81, 1, 0, 1, 1, 0, 1, 1, 0<, 81, 0, 0, 1, 0, 0, 1, 0, 0<,81, 1, 1, 1, 1, 1, 0, 0, 0<, 81, 1, 0, 1, 1, 0, 0, 0, 0<,81, 0, 0, 1, 0, 0, 0, 0, 0<, 81, 1, 1, 0, 0, 0, 0, 0, 0<,81, 1, 0, 0, 0, 0, 0, 0, 0<, 81, 0, 0, 0, 0, 0, 0, 0, 0<<となり,Flattenの効果で正方行列の形になる.この形式に変更することによっ
て,ListDensityPlotで図示できるようになる.
最後に,繰り返し数nをいろいろ変えてプロットすると,
133
Show@GraphicsArray@Block@8$DisplayFunction = Identity<,ListDensityPlot@−mS@#D,
PlotLabel → "繰り返し数=" <> ToString@#D, Mesh → False,
Frame → False, FrameTicks → FalseD & ê@ 82, 3, 4<DD;繰り返し数=2 繰り返し数=3 繰り返し数=4
となって,フラクタル図形ができる.
2.4.4 コッホ曲線
シェルピンスキー以外でよく引用されるフラクタル図形にコッホ曲線がある.コッホ曲
線もシェルピンスキー図形と同様に,入れ子構造つまり再帰構造を持つ関数で作成でき
る.初めに,1辺の長さが1の正三角形
ShowAGraphicsA9LineA980, 0<, 91ê2, è!!!!3 ë 2==E,
LineA991ê 2, è!!!!3 ë 2=, 81, 0<=E,
Line@881, 0<, 80, 0<<D=, Axes → TrueEE;
0.2 0.4 0.6 0.8 1
0.2
0.4
0.6
0.8
を準備する.各辺(#1)に対して,以下のような遅延置換の操作をする.
#1 ê. Line@8start_, finish_<D doline@start, finishD#1
ここで,startとfinishは各辺の両端を表し,Line[start,finish]を
134
doline@start_, finish_D := ModuleA8vec, normal<,vec = finish − start; H∗辺のベクトル∗Lnormal = Reverse@vecD 8−1, 1<
è!!!!3 ë 6;8Line@8start, start + vecê3<D, H∗辺の下端∗L
Line@8start + vecê 3, start + vecê2 + normal<D,H∗中央の辺を山形の2辺で置き換えた上部∗LLine@8start + vecê 2 + normal, start + 2 vecê3<D,H∗中央の辺を山形の2辺で置き換えた下部∗LLine@8start + 2 vecê3, finish<DH∗辺の上端∗L<E
で置き換える.右端のベクトルをstart,左端のベクトルをfinishとする辺を表すベク
トルvec=finish-startを定義する.後半の部分でLineが4回現れているが,これは
vecを3等分し,中央の部分を山形の2辺で置き換えていることによる.normalは,山形
を図形の外部になるようにした法線方向を表す.つまり,上方と下方の線分は
Line[start,start+vec/3]とLine[start+2 vec/3,finish]であり,残る山
形の2辺は,Line[start+vec/3,start+vec/2+normal]と
Line[start+vec/2+normal,start+2 vec/3]である.最後に,normalは山形に
突き出した法線方向で,vecに直交するベクトルである.今,原点から正三角形の上部頂
点へのベクトルは
ReverseA91 ê2, è!!!!3 ë 2= − 80, 0<E9 è!!!3
2,
12=
であるが,このことを考慮すると,
normal = % 8−1, 1< è!!!!3 ë 6;
Norm@normalD1
2 è!!!3はvecに直交し,その大きさが1 ë I2
è!!!!3 Mであることが分かる.これは元の三角形の1 ê3に
なる.つまり,高さ1 ë I2 è!!!!3 Mは元の三角形の高さè!!!!3 ë2の1 ê3である.normalがvecに直
交するしていることは,
normal.I91 ê2, è!!!!3 ë 2= − 80, 0<M
0
から確認できる.
さて,線分Line[0,0,1/2,è!!!!3 /2]に対してdolineを適用すると,
ShowAGraphicsAdolineA80, 0<, 91ê2, è!!!!3 ë 2=E, Axes → TrueEE;
135
0.1 0.2 0.3 0.4 0.5
0.2
0.4
0.6
0.8
となる.この操作を,初めの三角形を初期値として,Nestで繰り返し適用する関数にま
とめると,
fractalSnow[n_Integer]:=Show[Graphics[
Nest[(#1/.Line[start_,finish_] doline[start,finish])&,
Line[0,0,1/2,è!!!!3 /2],Line[1/2,
è!!!!3 /2,1,0],Line[
1,0,0,0],n]],
AspectRatio→Automatic,DisplayFunction→Identity]
となる.いくつかの例を示しておこう.
Show@GraphicsArray@fractalSnow@#D & ê@ 81, 2, 3, 4<DD;
ただし,nが5くらいになると相当時間がかかるばかりか,目で分かる複雑さの限界でもあ
ろう.
以下では,多彩なコッホ曲線を作ってみる.最初の例はコッホ曲線をいろいろな形でカ
ラー表示する.以下に示すプログラムは先の述べたコッホ曲線を生成する方法と基本的に
同じであるが,若干異なることに注意しよう.最初の三角形を
startTriangle =988−1, 0<, 81, 0<<, 981, 0<, 90, è!!!!!!3. ==, 990, è!!!!!!
3. =, 8−1, 0<==888−1, 0<, 81, 0<<,881, 0<, 80, 1.73205<<, 880, 1.73205<, 8−1, 0<<<と定義する.コッホ曲線は
136
fractalSnow2@line_ListD :=
Module@8d = line@@2DD − line@@1DD, norm, perp<,8norm, perp< = [email protected], 8d@@2DD, −d@@1DD<<;88line@@1DD, line@@1DD + d ê3<,8line@@1DD + d ê3,line@@1DD + d ê2 + perpênorm Sqrt@3D normê6<,8line@@1DD + d ê2 + perpênorm Sqrt@3D normê6,line@@1DD + 2 d ê3<,8line@@1DD + 2 d ê3, line@@2DD<< êê ND;
で記述できる.この関数は先に述べたfractalSnowと少々異なるが,機能は基本的に同
じなので理解できるものと思う.そこで,
kS@1D = startTriangle;
Do@kS@iD = Flatten@fractalSnow2 ê@ kS@i − 1D, 1D, 8i, 2, 5<Dとして,以下に,5個コッホ曲線を作る.これらのフラクタル図形を,
Flatten@kS@1D, 1D ê. 8x_, y_< → 8x, y< − 90, 1 ëè!!!!!!3. =88−1, −0.57735<, 81, −0.57735<, 81, −0.57735<,80, 1.1547<, 80, 1.1547<, 8−1, −0.57735<<
のように,90, 1 ëè!!!!3 =だけ下方に平行移動させる.なぜこのような平行移動するのかは以下の図を見て理解してほしい.実際,各コッホ曲線を,kS[5]からkS[1]へと徐々に小さ
くしてゆき,上に重ねるように図示すると,
ShowAGraphicsATableA9Hue@ Hi − 1Lê5D, H∗彩色.中心は赤になるようにした∗LPolygonAi IFlatten@kS@iD, 1D ê. 8x_, y_< → 8x, y< − 90, 1 ëè!!!!!!
3. =ME=,8i, 5, 1, −1<H∗大きい方から順に小さい方を描く∗LE, AspectRatio → AutomaticEE;
となる.
【問題】先に示した下方への平行移動をしなければ,どのように図形になるか,実際に確
137
かめよう.
次の例は,各辺を等分するのではなく,ランダムは割合で分けるようなコッホ曲線であ
る.つまり,先に示した通常のコッホ曲線では各線分を3等分したが,ここではランダム
な比で線分に区切るために,
RandomSpike@line_ListD :=
Module@8d, normD, perp, normPerp, ra<,d = line@@2DD − line@@1DD; H∗線分を選ぶ∗LnormD = Norm@dD; H∗その線分の長さを計算∗Lperp = 8d@@2DD, −d@@1DD<; H∗法線ベクトル∗LnormPerp = Norm@perpD; H∗法線ベクトルの長さ∗Lra = 2 Random@Integer, 80, 1<D − 1;H∗0 と1の乱数をy=2 x−1 を用いて,−1 と1に変換∗L88line@@1DD, line@@1DD + d ê3<,8line@@1DD + d ê 3,line@@1DD + d ê 2 + perpê normPerp Sqrt@3D normPerpê8 ra<,H∗8 は自己横断をしないように適当に設定した∗L8line@@1DD + d ê 2 + perpê normPerp∗Sqrt@3D normPerpê7 ra,line@@1DD + 2 d ê3<,8line@@1DD + 2 d ê3, line@@2DD<<D
とする.これまでのコッホ曲線と異なる点は,ra(等確率で-1か1をとる)のところだけで
ある.初めの正三角形を
startTriangle = 888−1, 0<, 81, 0<<,881, 0<, 80, Sqrt@3D<<, 880, Sqrt@3D<, 8−1, 0<<< êê N888−1., 0.<, 81., 0.<<,881., 0.<, 80., 1.73205<<, 880., 1.73205<, 8−1., 0.<<<として,RandomSpikeを適用すると,以下の図
SeedRandom@13455D;st@1D = startTriangle;H∗最初の三角形∗LDo@st@iD = Flatten@RandomSpike ê@ st@i − 1D, 1D, 8i, 2, 5<D;H∗最初の三角形から再帰的に5回ネストした図を作成∗LShow@GraphicsArray@
Graphics@Line ê@ #, AspectRatio → AutomaticD & ê@8st@1D, st@2D, st@3D, st@4D, st@5D<DD;
138
が得られる.もちろん乱数を使っているので,SeedRandomをとれば実行する度に異なっ
た図形が得られる.
もう少し複雑で彩色したコッホ曲線を作ってみよう.NestListで1,4,8回まで繰り返
して表示すると,
SeedRandom@1113D;Show@GraphicsArray@
Block@8$DisplayFunction = Identity<,Graphics@[email protected],
Table@8Hue@Random@DD, [email protected], Line ê@
Flatten@NestList@Flatten@RandomSpike ê@ #, 1D &,startTriangle, 4D, 1D<, 8#<D<,
AspectRatio → AutomaticD & ê@ 81, 4, 8<DDD;
のようになる.
【問題】上記のコ-ドで解析せよ.
2.5 フラクタルの定量的分析
複雑な形を表す代名詞になっているフラクタルの重要さは,次のように表現できる.物
の長さは,物に固有な性質してユニークに決まるはずだと思いがちであるが,実はそれは
単純で滑らかな形の場合であって,複雑な図形には当てはまらない.測定に使用する物差
しの尺度r(単位長さ)によって変化することを実に見事に表現した.フラクタル図形を定量的に定義する量は次元である.1次元,2次元,3次元のような通常の整数次元ではな
く,非整数で表されるフラクタル次元のことである.まず,通常の整数次元をどのように
定義すべきか考えよう.単純な図形の次元を求める方法を考える.長さ1(大きさ1は以下
の議論に関係ないが)の直線を考えよう.海岸線の場合と同様,物差しの尺度を1とする
と,その単位では長さは1である.物差しの尺度を1 ê2に縮小するとその物差しの単位長さ
は1 ê 2になるので,2回物差しを当てなければならない.あるいは,その縮尺で出来た元
の直線に相似な直線が2個できる.その単位での長さは2となるが,実際の長さはH1 ê 2L 2 = 1となることから元の長さ1に等しい.1辺の長さが1の正方形の面積の場合,物
差しの尺度を1 ê 2と縮小すると,その物差しでの面積は1 ê4になる.すると,4個の小さな
相似な正方形ができるので,その単位での面積は4になる.ただし,実際の面積は
139
H1 ê 4L 4 = 1となることから元の面積1に等しい.このように,同じ物でも物差しの単位によ
ってその大きさを表す数値は異なるが,大きさ自体は変ることはない.
以上のことを一般化して,物差しのスケールをr( r < 1)とすると,そのスケール(あ
るいは,単位面積,単位体積)で測ると,NHrL回当てなければならなったとする.あるいは,そのスケールで出来た元の図形に相似な図形の個数をNHrLとする.たとえば,r = 1 ê 2とすると,直線ではNHrL = 2,正方形ではNHrL = 4である.この時,次元Dは
NHrLを用いて, D = log NHrLÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅlogH1êrLあるいは,
NHrL º r-D
と表せる.このように,物理量あるいは現象がべき関数で表される時,べき則にしたがう
という.べき指数Dをフラクタル次元と呼んでいる.
2.5.1 フラクタル次元
フラクタル次元は入れ子構造を定量的に表す指数で,フラクタル図形の複雑さを表して
いる.今,簡単な例として,長さ1の線分を考える.それをlevel0として,次のレベルで
の図形をlevel1として,
level0 = [email protected], Line@880, 0<, 81, 0<<D<;level1 [email protected], Line@880, 0<, 80, 1 ê6<, 81ê6, 1ê6<,81 ê6, 1ê3<, 81ê 3, 1ê3<, 81ê2, 1ê3<, 81ê2, 1ê6<,81 ê3, 1ê6<, 81ê 3, 0<, 81ê2, 0<, 82ê3, 0<,82 ê3, −1 ê6<, 81ê2, −1ê6<, 81ê2, −1ê3<, 82ê3, −1ê3<,85 ê6, −1 ê3<, 85ê6, −1ê6<, 81, −1ê6<, 81, 0<<D<;Show@GraphicsArray@Block@8$DisplayFunction = Identity<,8Show@Graphics@level0D, Axes → TrueD,Show@Graphics@level1D, Axes → TrueD<DDD;
0.2 0.4 0.6 0.8 1
−1
−0.5
0.5
1
0.2 0.4 0.6 0.8 1
−0.3−0.2−0.1
0.10.20.3
としよう.コッホ曲線の場合と同様に,level1の各線分に対してこの操作を続けていく
とフラクタル図形になる.この時,右図では長さr = 1 ê 6の線分がNHrL = 18個できるの
で,D = log NHrL ê logH1 ê rLに代入すると,フラクタル次元は
140
Log@18DLog@1 êH1 ê6LD êê N1.61315
と求まる.
2.5.2 コッホ曲線の辺の長さと個数
これまでコッホ曲線の描き方を述べが,その作成方法からフラクタル次元は,
Log@4DLog@1 êH1 ê3LD êê N1.26186
である.
フラクタル次元がコッホ曲線の周囲の長さ,辺の個数とどのような関係にあるか,定量
的な分析を行おう.具体的にコッホ曲線の周囲の長さを計算し,それからフラクタル次元
をグラフィックスを用いて求める.最初の段階(n = 0)でのコッホ曲線は三角形で,前に
定義したfractalSnowを使えば分かるように,
fractalSnow@0D@@1DD9LineA980, 0<, 9 12,
è!!!32
==E,LineA99 1
2,
è!!!32
=, 81, 0<=E, Line@881, 0<, 80, 0<<D=となり,プリミティブLineで三角形が作成される.コッホ曲線fractalSnow(最初の段
階では三角形)の周囲の長さを求めるために最初にすべきことは上記のリストから各辺の
ベクトルを表すリストを取り出すことで,これは置換を利用すれば,
fractalSnow@0D@@1DD ê. Line@x_ListD → x9980, 0<, 9 12,
è!!!32
==, 99 12,
è!!!32
=, 81, 0<=, 881, 0<, 80, 0<<=と得られる.このリストの要素はコッホ曲線(三角形)の頂点を結ぶ線分を表し,たとえ
ば,980, 0<, 91 ê 2, è!!!!3 ë2==は原点と上の頂点を結ぶ辺のベクトルを表す.次に,後の処理の都合上,
Flatten@HfractalSnow@0D@@1DD ê. Line@x_ListD → xL, 0D9980, 0<, 9 12,
è!!!32
==, 99 12,
è!!!32
=, 81, 0<=, 881, 0<, 80, 0<<=とし,各辺の長さを求める.今,H#@@2DD − #@@1DDL & ê@888a1, b1<, 8c1, d1<<, 88a2, b2<, 8c2, d2<<<88−a1 + c1, −b1 + d1<, 8−a2 + c2, −b2 + d2<<
141
となることを考慮すると,8a1, b1<から8c1, d1<へ向かうベクトル,8a2, b2<から8c2, d2<へ向かうベクトルが求められる.これらの座標を結ぶベクトルの長さは,Normを使って,
Norm ê@ HH#@@2DD − #@@1DDL & ê@888a1, b1<, 8c1, d1<<, 88a2, b2<, 8c2, d2<<<L9"########################################################################Abs@−a1 + c1D2 + Abs@−b1 + d1D2 ,"########################################################################Abs@−a2 + c2D2 + Abs@−b2 + d2D2 =となるので,2辺の長さが計算できる.以上のことから,n = 0でのコッホ曲線(三角形)
の3辺の長さは,
Norm ê@ HH#@@2DD − #@@1DDL & ê@
Flatten@HfractalSnow@0D@@1DD ê. Line@x_ListD → xL êê N, 0DL81., 1., 1.<と同じ長さ1にある(当然であるが).同様に,n = 1でのコッホ曲線では,
Norm ê@ HH#@@2DD − #@@1DDL & ê@
Flatten@HfractalSnow@1D@@1DD ê. Line@x_ListD → xL êê N, 1DL80.333333, 0.333333, 0.333333, 0.333333, 0.333333, 0.333333,0.333333, 0.333333, 0.333333, 0.333333, 0.333333, 0.333333<
になる.12個の辺があるが,それらの辺はコッホ曲線の作成方法からすべての同じ長さに
なっている.尚,Flattenを適用するレベルに注意しよう.n = 0はレベル0(何もしな
い),n = 1はレベル1である.
以上のことをまとめて,各辺の長さを求め,それらを足し合せてコッホ曲線の周辺の全
長をn = 7まで求めると,
lenghthKoch =
Table@Plus @@ Norm ê@HH#@@2DD − #@@1DDL & ê@ Flatten@HfractalSnow@jD@@1DD ê.Line@x_ListD → xL êê N, jDL, 8j, 0, 6<D83., 4., 5.33333, 7.11111, 9.48148, 12.642, 16.856<
となる.同様に,辺の数を求めると,
numLine =
Table@Length@HH#@@2DD − #@@1DDL & ê@
Flatten@HfractalSnow@jD ê. Line@x_ListD → xL@@1DD êê N,jDLD, 8j, 0, 6<D83, 12, 48, 192, 768, 3072, 12288<
となる.以上のようにして求めた各レベル(n = 7まで)におけるコッホ曲線の周辺の全長
と辺の総数を方対数グラフで表示すると,
142
Show@GraphicsArray@Block@8$DisplayFunction = Identity<,LogListPlot@#@@1DD, PlotJoined → True, PlotLabel →
If@#@@2DD 1, "コッホ曲線の全長", "コッホ曲線の辺数"D,Frame → True, FrameLabel → 8"n", None<,PlotRange → AllD & ê@88lenghthKoch, 1<, 8numLine, 2<<DDD;
1 2 3 4 5 6 7
n
3
57
10
15
コッホ曲線の全長
1 2 3 4 5 6 7
n
10
100
1000
10000コッホ曲線の辺数
となる.
これらの図から分かるように,両者ともに指数関数eb n(bは正の定数)で近似でき
る.その指数はFindFitを用いて,
b ê. FindFit@Log@lenghthKochD, a + b x, 8a, b<, xDb ê. FindFit@Log@numLineD, a + b x, 8a, b<, xD0.287682
1.38629
となった.以上のことから,辺の全長LHnLおよび辺の数NHnLはそれぞれ, LHnL = 3 e0.287682 n
NHnL = 3 e1.38629 n
と表せる.ここで,LH0L = 3,NH0L = 3となることを利用している.この結果から,コッホ
曲のフラクタル次元を求めることができる.任意のnに対してr = H1 ê 3Lnとなるので,フラクタル次元は
D =log NHnLÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅn log 3
から求めることができる.上記の結果を代入すると,nに値に関係なく,
Log@ 1.38629 nDLog@3nD êê PowerExpand
1.26186
となり,理論値log4 ê log3にほぼ等しいことが確認できる.
さて,フラクタル次元NHnL º r-Dを用いると,物差しの尺度をrとすると,全長はLHnL º rd-Dと表せる.ここで,dは空間の次元を表すが,ここでは長さを対象にしている
のでd = 1である.これにr = H1 ê3LnとDの値を代入すると,
LHnL º rd-D = 30.26186 n = e0.287683 n
143
となる.これは数値的に求めた結果に一致する.
次に,辺の数NHnLについて調べよう.フラクタル次元と同様に考えて,表示すると,ListPlotATableA Log@3 0.287682 nD
Log@3nD , 8n, 150<E, PlotJoined → True,
PlotRange → 80, 1<, FrameLabel → 8"n", "D1"<, Frame → TrueE;
0 20 40 60 80 100120140
n
0.2
0.4
0.6
0.8
1
1D
となり,ある値の収束していくことが分かる.実際,nの値が大きくなると,
Log@3. 0.2877 nDLog@3nD êê PowerExpand êê Simplify
0.261876 +1.n
となって,0.261876に近づく.この値は,Dから1を引いた値である.
2.5.3 カントール集合
コッホ曲線と同様,しばしば引用されるフラクタルにカントール集合がある.カントー
ル集合の実例は,後で述べるがカオスを生じるロジスティック写像などで見られる.ま
ず,長さ1の直線を準備する.下図に示すように,ステップ1の操作で,長さ
r(0 < r < 1)の左右の部分2個を残し,中央に残る長さ1 - 2 rの部分は取り除く.ステッ
プ2ではステップ1で残った左右の部分に対して,同じ操作を施し,それぞれ長さr2の部分
を2個づつ,合計4個残す(楕円で囲った線分).同じ操作を無限に続けてできた図形がカ
ントール集合である.
ステップ1
ステップ2
r
2r
r
1
2r2r2r
3r 3r
ステップ3
144
この集合が普通の意味での集合でないことを示そう.このため,r=1/3の場合に,カントール集合の線分の長さを計算しよう.上図を参考に,Limitを使えば,
1 − LimitA‚j=1
n
2j−1 1
3j, n → ∞E
0
となるように,nが大きくなるにつれて長さは0となるので,点の集合と思える.そこ
で,nの関数としてどのように0に近づくか調べると,
ListPlotALogATableA1 − ‚j=1
n
2j−1 1
3j, 8n, 100<EE,
PlotRange → All, AxesLabel → 8"n", "対数誤差"<E;20 40 60 80 100
n
−40
−30
−20
−10
対数誤差
となり.0への接近の仕方を定量的に調べると,
FindFitALogATableA1 − ‚j=1
n
2j−1 1
3j, 8n, 100<EE, a + b n, 8a, b<, nE8a → −2.4869×10−15, b → −0.405465<
となるように,e-0.405 nに従うことが分かる.しかし,フラクタル次元からは,カントー
ル集合は点の集まりに見えて,実際にはそうではない.点(次元0)と線分(次元1)の中
間的な存在で,フラクタル次元は0より大きく1より小さい(0 < D < 1).実際に,カン
トールのフラクタル次元は
Log@3DLog@1 êH1 ê2LD êê N1.58496
である.
さて,0から1までの範囲にあるどのような実数も, p = 1 ê2として,
a1 p + a2 p2 + a3 p3 + ∫
と表せる.ここで,a1,a2,a3,...は0か1である.これは,10進数で表された実数の
2進数展開である.カントール集合は,上記の作成方法を考えれば,p = 1 ê3とした場合に
対応することが分かる. pを複素数にまで拡張した場合,p = 0.65 - 0.3 i等の複素数とした
時に得られる図形はGoffinet dragonと呼ばれている.
145
nステップ後の点集合は,n - 1ステップ後の点集合に0とpnを追加したものである.最
初のステップでは,0と pだけに点を置き,
CantorPoints@1, p_D = 80, p<;とする.次のステップでは,Outerが
Outer@Plus, 8a<, 8b, c<D88a + b, a + c<<となるような作用することに注意すると,
Outer@Plus, 8p2<, CantorPoints@1, pDD88p2, p + p2<<となる.更に,
Join@CantorPoints@1, pD,Outer@Plus, 8p2<, CantorPoints@1, pDD@@1DDD80, p, p2, p + p2<
となることに留意して,再帰的な関数
CantorPoints@n_Integer, p_D :=
CantorPoints@n, pD = Join@CantorPoints@n − 1, pD,Outer@Plus, 8pn<, CantorPoints@n − 1, pDD@@1DDD
を導入する.いくつかの例を示すと,
CantorPoints@#, pD & ê@ 81, 2, 3<880, p<, 80, p, p2, p + p2<,80, p, p2, p + p2, p3, p + p3, p2 + p3, p + p2 + p3<<となる.この結果は,ステップを増すにつれてどのような点が追加されていくかを示して
いる.最初0と pにあった点に,次の段階ではそれぞれにp2を足した0 + p2,p + p2を追
加している.最初に示したカントール集合の作成方法との対応関係は,もう少し読み進め
ば明らかになろう.
pが複素数になることも考慮して,以上のように点列の横軸を実部,縦軸を虚部にして
表示する関数を
CantorPicture@n_, p_D :=
Show@Graphics@Point@8Re@#D, Im@#D<D & ê@ CantorPoints@n, pDD,DisplayFunction → IdentityD;
とする.最初の例として, pが実数の場合をとり上げよう.p = 1 ê 3として,図の位置を移動させて,n = 5まで同時に表示すると,
146
DisplayTogether@Table@CantorPicture@n, 1ê3.D ê.Point@8x_, y_<D → [email protected], Point@8x, y − n<D<,8n, 5<D, PlotRange → All, AspectRatio → 0.2D;
となって,カントール集合に等価なフラクタル図形が描ける.各ステップでのグラフ
は,y - nとして下方に平行移動した.尚,PlotRange→Allに指定がないと端にある点が
欠ける.次に,pを複素数にすると,実部と虚部を2次元のグラフに表示すると,
Show@GraphicsArray@8CantorPicture@9, 1.0 + 0.2 D,CantorPicture@12, 0.65 − 0.3 D<DD;
となる.右図は確かにドラゴンを連想させる.
2.5.4 2次元カントール集合
以上に述べた標準的なカントール集合を2次元に拡張しよう.今,正方形 H0, 1LäH0, 1Lの中において,通常のカントール集合を参考に2次に拡張すると,2次元カントール集合の点
の位置は,
xHtL =t1ÅÅÅÅÅÅ2 +
t3ÅÅÅÅÅÅÅ22 +
t5ÅÅÅÅÅÅÅ23 +
t7ÅÅÅÅÅÅÅ24 +
t9ÅÅÅÅÅÅÅ25 + ∫
yHtL =t2ÅÅÅÅÅÅ2 +
t4ÅÅÅÅÅÅÅ22 +
t6ÅÅÅÅÅÅÅ23 +
t8ÅÅÅÅÅÅÅ24 +
t10ÅÅÅÅÅÅÅÅ25 + ∫
と表せる.ここでは2進数展開に限定しよう.したがって,係数t j H j = 1, 2, 3, ...Lは0ある
いは1である.さて,10進数で表した整数nを2進数展開した時の各桁の数字をリスト形式
で返すIntegerDigitsを用いると,たとえば,2個の整数に対して,
IntegerDigits@82, 5<, 2D881, 0<, 81, 0, 1<<とすると,2 = 1 μ 21 + 0 μ 20と,5 = 1 μ 22 + 0 μ 21 + 1 μ 20を2進数展開した時の係数のリス
トが得られる.さらに,
IntegerDigits@82, 5<, 2, 3D
147
880, 1, 0<, 81, 0, 1<<とすると,すべてのリスト長が3になるように左側に0を挿入する.以上に示したInte-
gerDigitsの性質を使うと,
t1 22 + t3 21 + t5 20
とした場合に,係数のリスト8t1, t3, t5<が得られるので,これを23で割り,
t12 +
t322
+t523
とすれば,カントール集合の点に位置xHtLが求められる.たとえば,0から7までの整数
は,
With@8n = 3<,IntegerDigits@Range@0, 2n − 1D, 2, nDD880, 0, 0<, 80, 0, 1<, 80, 1, 0<, 80, 1, 1<,81, 0, 0<, 81, 0, 1<, 81, 1, 0<, 81, 1, 1<<
となる.さらに,係数を操作すると,カントール集合の最初の8個の点は9 12,
1
22,
1
23=.# & ê@ IntegerDigits@Range@0, 23 − 1D, 2, 3D90, 1
8,
14,
38,
12,
58,
34,
78=
となることが確かめられる.係数の係数t jの添え字の付け方に留意すると,2次元カン
トール集合の各点に位置は,
Cantor2D@n_D := Cantor2D@nD = Evaluate@8Table@Which@OddQ@iD, 2−Hi+1Lê2, EvenQ@iD, 0D, 8i, n<D.#,H∗x座標の位置∗LTable@Which@EvenQ@iD, 2−iê2, OddQ@iD, 0D, 8i, n<D.#<H∗y座標の位置∗LD & ê@ IntegerDigits@Range@0, 2n − 1D, 2, nD;
と表せる.
いくつか例を示すと,
Cantor2D[1],Cantor2D[2]9980, 0<, 9 12, 0==, 980, 0<, 90, 1
2=, 9 1
2, 0=, 9 1
2,
12===
のようになる.各点をグラフにするには,
Point@#D & ê@ Cantor2D@2D9Point@80, 0<D, PointA90, 12=E,
PointA9 12, 0=E, PointA9 1
2,
12=E=
とすればよいので,たとえば,
148
Show@GraphicsArray@Graphics@Point@#D & ê@ Cantor2D@#D, AspectRatio → 1D & ê@86, 9, 12<DD;
となる.
今,x座標とy座標を別々に考えよう.LebesgueCantor[n]の第1要素がx座標,第2要
素がy座標であることを考慮すると,各点のx座標あるいはy座標を次々に線で結んで表示
する関数は,
xyPicture@n_, xy_D :=
ListPlot@Transpose@Cantor2D@nDD@@If@xy === "x", 1, 2DDD,PlotJoined → True, AxesLabel → 8"t", xy<,PlotLabel → StyleForm@xy <> "HtL", FontFamily → "Times",
FontSize → 10D, DisplayFunction → IdentityD;と記述できる.たとえば,
Show@GraphicsArray@8xyPicture@6, "x"D, xyPicture@6, "y"D<DD;
10 20 30 40 50 60t
0.2
0.4
0.6
0.8
x xHtL10 20 30 40 50 60
t
0.2
0.4
0.6
0.8
y yHtL
のようになる.ここで,横軸は自然数になる.平面内の各点を結んだ図を作成しよう.
Show@GraphicsArray@Graphics@Line@Cantor2D@#DD,PlotRange → All, AspectRatio → Automatic,
PlotLabel → StyleForm@"2D Cantor Hn=" <>
ToString@#D <> "L", FontFamily → "Times",
FontSize → 10DD & ê@ #DD & ê@ 882, 4<, 86, 8<<;
149
2D Cantor Hn=2L 2D Cantor Hn=4L
2D Cantor Hn=6L 2D Cantor Hn=8L
これらの図を彩色し,重ね合わせて表示すると,
Show@Graphics@Table@[email protected], Hue@iê8D, Line@Cantor2D@iDD<,8i, 2, 8<DD, AspectRatio → AutomaticD;
となる.
最後に,このような2次元カントール集合の点を結んだ線の長さにどのような規則性が
あるか調べよう.標準的なカントール集合と同じくべき則が現れるだろう.まず,Leb-
esgueCantor[2]を用いて作った3点の座標を,
Partition@Cantor2D@2D, 2, 1D9980, 0<, 90, 12==, 990, 1
2=, 9 1
2, 0==, 99 1
2, 0=, 9 1
2,
12===
のように2個の対になるようにリストを分割する.Subtractは引き算をするので,
Apply@Subtract@##D &, Partition@Cantor2D@2D, 2, 1D, 81<D990, −12=, 9−
12,
12=, 90, −
12==
150
とすると,2点間を表すベクトルが求まる.後は,各要素#のSqrt[#.#]を求めればよ
い.まとめると,n = 3からn = 12までの各nに対してその長さを求める場合は,
myCL = With@8n = 12<,Table@Plus @@ Apply@ Sqrt@#.#D &@Subtract@##DD &,
Partition@Cantor2D@kD, 2, 1D, 81<D, 8k, 3, n<DD êêN82.67705, 4.91189, 7.01584, 11.4982, 15.8189,
24.8199, 33.5216, 51.5487, 68.9829, 105.051<となる.これをグラフ表示すると,
ListPlot@Log@10, myCLD, PlotJoined → TrueD;
4 6 8 10
0.5
0.75
1.25
1.5
1.75
2
となることから分かるように,指数関数を当てはめることができる.そこで,以上のデー
タの対数をとって,線形式に当てはめると,
Clear@nD;FindFit@Log@myCLD, a + b n, 8a, b<, nD8a → 0.756421, b → 0.39488<
となり,e0.39488 nのように指数関数的に増加することが分かる.この結果を標準的なカン
トール集合で得られた結果3 e0.287682 nと比較すると,2次元のため指数が大きくなってい
ることが分かる.これを用いると,フラクタル次元は
LHnL º rd-D = 2H2-dL n º e0.39488 n
から,D = 1.4303と求められる.
2.6 複雑なフラクタル図形
これまで述べた以外の代表的なフラクタルを紹介する.
2.6.1 ニュートン法による複素根
方程式 f HxL = cの根を求めるニュートン法の公式をとり上げる.ニュートン法は最適化のための1メソッドであるが,ここではフラクタルとの関係で,その根の興味ある特徴を
グラフィックスで表現しよう.ここでとり上げる方程式は,
xn = 1
151
である(nは整数である).変数は繰り返し回数iと初期値である.ニュートン法から根を
求める公式は, f HxL = xn - 1から,
xi+1 = xi -xin-1ÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅÅn xin-1
と書ける.最適化の所で述べたことと異なる点は,初期値,根を複素数に拡張している点
である.FixedPointを用いて根を求めるために,複素数の初期値x1から,x2,x3,...と
iを増やし,最大で20回,つまりx20になるまで繰り返そう.求まった解も一般には複素数
x*になる.x*の偏角Argを求めるプログラムを,Compileを用いて,
cNewtonF = Compile@88n, _Integer<, 8init, _Complex<<,Arg@FixedPoint@# − H#n − 1LêHn #n−1L &, init, 20DDD;
と定義しよう.変数はnと初期値x1のinitである.一例を示すと,
cNewtonF@4, −0.5 + 0.5 D−0.785398
となる.いろいろな初期値x1を設定して求めた複素根の偏角Argを計算し,偏角の大きさ
に応じてListDensityPlotでカラー表示すると,
Show@GraphicsArray@Block@8$DisplayFunction = Identity<,Table@ListDensityPlot@Table@cNewtonF@n, x + yD,8x, −1.0, 1.0, 0.01<, 8y, −1.0, 1.0, 0.01<D, Frame → True,
Mesh → False, MeshRange → 88−1.0, 1.0<, 8−1.0, 1.0<<,ColorFunction → Hue, PlotLabel → "n=" <> ToString@nDD,8n, 1, 4<DD, GraphicsSpacing → 0.1D;
−1−0.50 0.5 1−1
−0.5
0
0.5
1n=1
−1−0.50 0.5 1−1
−0.5
0
0.5
1n=2
−1−0.50 0.5 1−1
−0.5
0
0.5
1n=3
−1−0.50 0.5 1−1
−0.5
0
0.5
1n=4
のようになる.フラクタルに関する書物でしばしば引用される図で,フラクタルの特徴で
ある入れ子構造が見られる.最後に,
Clear@cNewtonFD;としておこう.
もう一つ例を述べる.今,n = 7として,
newton2@z_D = z − Hz7 − 1LêD@z7 − 1, zD;
152
と定義する.一般的に,初期値によって収束するまでの繰り返し数が異なる.そこ
で,newton2の固定点に至る繰り返し数に着目する.その値はFixedPointListで求め
ることができるので,関数
lfpl@x_D :=
Length@FixedPointList@Function@z, newton2@zDD, xDDを導入しよう.ここで,x,yをそれそれ-1から1までの等区間に分けて計算し,
With@8intervals = 151<,lengthNewton = Table@lfpl@x + I yD,8y, −1., 1., 2 êintervals<, 8x, −1., 1., 2êintervals<DD;
と求めておく.たとえば,lengthNewtonには,
lengthNewton@@1DD êê Short@#, 2D &814, 14, 16, 17, 19, 23, 21, 37, 25, 25, 27,32, 128 , 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9<
のような値が入っている.この結果を繰り返し数に応じてListDensityPlotで表示する
と,
ListDensityPlot@lengthNewton,Mesh → False, ColorFunction → HHue@5 #D &L,MeshRange → 88−1.0, 1.0<, 8−1.0, 1.0<<D;
−1 −0.5 0 0.5 1−1
−0.5
0
0.5
1
となる.
【問題】nをいろいろ変えて計算しよう.たとえば,n = 4の場合どのような結果になるか
調べよ.
2.6.2 マンデルブロー図
次に,マンデルブロー図(マンデルブロー集合とも呼ぶ)をとり上げる.同時に,コン
パイルの効果を確かめる.フラクタル図形は自己相似性を利用して作られるので,どの部
分をとってみても,縮尺が異なるだけで元の図形と同じ構造を持つ.マンデルブロ集合も
153
ニュートン法の場合と同様に,図形は驚くばかりに複雑であるが,その描き方は逆にたい
へん簡単である.複素数zをx,yを実数としてz = x + i yと表そう.いま,適当な初期値
z0から出発して,
zn = zn-12 + c
にしたがって順にznを求める.この方程式は変数を複素数に拡張したロジスティック写像
であるが,このことからも複雑な挙動が想像できる.ここで,c = cx + i cyは適当な複素数
で,実数部をcx,虚数部をcyとする.そして,定数k,nkを設定して, » zn » > kとなる
か,あるいは,n < nkが満たされない限り上式を繰り返す.この時,有限な値に留まるよ
うな集合がマンデルブロ集合である.cx,cyの値をいろいろ変えて,複素平面8cx, cy<に最後のnの値に応じて濃淡を描いた図をマンデルブロ図という.結果が変わらなくなるま
で繰り返しを実行して最終的な結果のリストを作成するFixedPointListの典型的な応
用例である.
最初にコンパイルを用いないで,プログラムしてみよう.プログラムの終了条件である
パラメータは,以下ではnk = 50とする.特に後者の条件はオプションで指定でき,純関数
を用いてSameTest→(Abs[#2]>k&)とする.ここで,#2は2番目の引数であるが,これ
は判定でAbs[#2]>2.0となる1つ前の変数までリストに取り入れることを意味してい
る.各8cx, cy<の値に対して,高さに相当する繰り返し数nは,FixedPointListの長さ
Lengthで決まる.したがって,初期値を複素数cとし,それとkを変数とするプログラム
は
ncMandel@c_Complex, k_RealD := Module@8<, Length@FixedPointList@#2 + c &, c, 50, SameTest → HAbs@#2D > k &LDDD
と書ける.DensityPlotで,kの値を変えたマンデルブロ図を2個同時に描き,同時に計
算時間も調べると,
Timing@Show@GraphicsArray@Block@8$DisplayFunction = Identity<,DensityPlot@ncMandel@cx + cy I, #D, 8cx, −2.2, 0.5<,8cy, −1.2, 1.2<, Mesh → False, AspectRatio → Automatic,
Frame → False, ColorFunction → HGrayLevel@1 − #D &L,PlotPoints → 128D & ê@ 80.75, 1, 2<DDD;D
154
86.89 Second, Null<となった.ここで,繰り返し数に応じたグレーレベルで表示した.尚,各方向へサンプル
点数PlotPointsを増加すると詳細まで鮮明な図が描けるが,それの伴い計算時間も増え
る.
次の同じ計算を行うプログラムにCompileを使ってみよう.プログラムは,
cMandel = Compile@88c, _Complex<, 8k, _Real<<, Length@FixedPointList@#2 + c &, c, 50, SameTest → HAbs@#2D > k &LDDD
CompiledFunction@8c, k<,Length@FixedPointList@#12 + c &, c, 50,
SameTest → HAbs@#2D > k &LDD, −CompiledCode−Dである.コンパイルなしの場合と同様の条件(カラー表示にした)で計算すると,
Timing@Show@GraphicsArray@Block@8$DisplayFunction = Identity<,DensityPlot@cMandel@cx + cy I, #D, 8cx, −2.2, 0.5<,8cy, −1.2, 1.2<, Mesh → False, AspectRatio → Automatic,
Frame → True, FrameTicks → None, FrameStyle →[email protected], [email protected]<, ColorFunction →[email protected] + 0.6 #D &L, PlotPoints → 128D & ê@80.75, 1, 2<D, GraphicsSpacing → 0.1DD;D
82.844 Second, Null<にように大変早く計算が終了する.尚,カラー表示において,n = 10以上になれば
Hue[1]と赤になる.あるいは,FixedPointListを使わないで,単純に繰り返し数をカ
ウントし,
cMandel2 = Compile@8x, y, k<, Module@8z, n = 0<,z = x + y; While@Abs@zD < k && n ≤ 50, z = z2 + Hx + yL;
++nD; nDDCompiledFunction@8x, y, k<,Module@8z, n = 0<, z = x + y; While@Abs@zD < k && n ≤ 50,
z = z2 + Hx + yL; ++nD; nD, −CompiledCode−Dとするこも可能である.実行すると,
155
Timing@DensityPlot@cMandel2@cx, cy, 2D,8cx, −2.2, 0.5<, 8cy, −1.2, 1.2<, Mesh → False,
AspectRatio → Automatic, Frame → False,
ColorFunction → HGrayLevel@1 − #D &L, PlotPoints → 128D;D
80.89 Second, Null<となる.
最後に,グラフィックスとしてマンデルブロー図の面白さを強調するために
Plot3D@cMandel2@cx, cy, 2D, 8cx, −2.2, 0.5<,8cy, −1.2, 1.2<, Mesh → False, Axes → False,
Boxed → False, AspectRatio → Automatic,
ColorFunction → HHue@#D &L, PlotPoints → 256D;
としておこう.
以上に述べた方法はMathematicaのカーネルだけで直接実行できる方法であるが,それ以
外にもいくつかの方法がある.本書では触れないが,プログラムをC言語で書いて,Math-
Linkで呼び出す方法がある.この方法によると,コンパイルを用いた上記の方法より更に
早く実行できる.最後に,
Clear@ncMandel, cMandel, cMandel2D;としておこう.
156
2.6.3 ジュリア集合
マンデルブロー集合に関連するフラクタルにジュリア集合がある.ジュリア集合と
は,固定した8cx, cy<に対して,いろいろ初期値を変えて,上記の方法で求まった最後のznを,複素平面上に描いた集合である.プログラムはマンデルブローの場合とほとんど同
じで,特に説明の必要はないだろう.
cJulia =
Compile@8x, y, 8c, _Complex<, 8k, _Real<<, Module@8z, n = 0<,z = x + y; While@Abs@zD < k && n ≤ 50, z = z2 + c; ++nD; nDD
CompiledFunction@8x, y, c, k<, Module@8z, n = 0<, z = x + y;
While@Abs@zD < k && n ≤ 50, z = z2 + c; ++nD; nD, −CompiledCode−D以下に,いくつかの8cx, cy<に対して同時にジュリア集合を表示すると,
Timing@Show@GraphicsArray@DensityPlot@cJulia@x, y, #, 2D, 8x, −1.5, 1.5<, 8y, −1.5,
1.5<, Mesh → False, Frame → False, ColorFunction → Hue,
DisplayFunction → Identity, PlotPoints → 256D & ê@[email protected] − 0.7 D, [email protected] − 0.5 D<DD;D
84.266 Second, Null<のようになる.グラフィックスとしてジュリア集合の面白さを強調するために
Plot3D@−cJulia@x, y, 0.3 − 0.5 , 2D, 8x, −1.5, 1.5<,8y, −1.5, 1.5<, Axes → False, Boxed → False,
Mesh → False, ColorFunction → HGrayLevel@#D &L,PlotPoints → 256, ViewPoint −> 80.742, −1.370, 3.004<D;
157
とした.最後に,
Clear@cJuliaDとしておく.
2.6.4 ヒルベルト曲線
最後に,ヒルベルト曲線と呼ばれる3次元的なフラクタル構造をもつ複雑な曲線を説明
する.3次元的空間内にある点を考え,その点を以下に述べるルールに従って移動さ
せ,その軌跡を描く.どのように移動させるかを文字列に対応させ,
"F":前進
"B":後進
とする.今,点が8a, b, c<にあるとすると,8a, b, c< + HFirst ê@ IdentityMatrix@3DL81 + a, b, c<はx方向へ1前進することを表し,8a, b, c< − HFirst ê@ IdentityMatrix@3DL8−1 + a, b, c<はy方向へ1後進(-1前進)になる.もし,単位行列IdentityMatrix[3]に以下の行列
を掛けて同様の演算を行うと,8a, b, c< +HFirst ê@ [email protected], 0, 1<, 80, 1, 0<, 8−1, 0, 0<<L8a, b, 1 + c<は今度はz方向への前進移動になる.このように異なる行列を用意しておけば,任意の方
向への前進,後進の移動が可能になる.
フラクタルの特徴は入れ子構造であるが,この入れ子構造を移動を表す文字列に組み込
む.今,
158
recursion =
"X" → 8"", "", "X", "F", "", "", "X", "F", "X", " ", "F",
"", "∆", "∆", "X", "F", "X", "Ω", "F", " ", "∆", "∆",
"X", "F", "X", " ", "F", "∆", "X", " ", "∆"<;のような文字列を考えよう."F"と"B"は現れた場所で点が移動することにな
る."Ω",""などは回転を表している.この例では"F"だけを用いているので,7回前進
することになる.ここで,"X"が右辺を文字列のリストを表すが,このことを利用する
と,
Nest@# ê. recursion &, Characters@"X"D, 2D88, , 8, , X, F, , , X, F, X, , F, , ∆,∆, X, F, X, Ω, F, , ∆, ∆, X, F, X, , F, ∆, X, , ∆<,F, , , 8, , X, F, , , X, F, X, , F, , ∆, ∆, X,F, X, Ω, F, , ∆, ∆, X, F, X, , F, ∆, X, , ∆<,F, 8, , X, F, , , X, F, X, , F, , ∆, ∆, X, F, X,
Ω, F, , ∆, ∆, X, F, X, , F, ∆, X, , ∆<, , F,
, ∆, ∆, 8, , X, F, , , X, F, X, , F, , ∆, ∆, X,F, X, Ω, F, , ∆, ∆, X, F, X, , F, ∆, X, , ∆<,F, 8, , X, F, , , X, F, X, , F, , ∆, ∆, X, F, X,
Ω, F, , ∆, ∆, X, F, X, , F, ∆, X, , ∆<, Ω, F,, ∆, ∆, 8, , X, F, , , X, F, X, , F, , ∆, ∆, X,
F, X, Ω, F, , ∆, ∆, X, F, X, , F, ∆, X, , ∆<,F, 8, , X, F, , , X, F, X, , F, , ∆, ∆, X, F, X,
Ω, F, , ∆, ∆, X, F, X, , F, ∆, X, , ∆<, , F,∆, 8, , X, F, , , X, F, X, , F, , ∆, ∆, X, F, X,
Ω, F, , ∆, ∆, X, F, X, , F, ∆, X, , ∆<, , ∆<<は右辺の"X"にネスティングしている.これが文字列の入れ子構造になり,後は前進,後
進の移動の様子とLineで描けばよい.
以上のことをプログラムにまとめておこう.
nestedCurve3D@n_Integer?PositiveD :=
Module@8axiom = "X", = 80, 0, 0<, = IdentityMatrix@3D<,Prepend@DeleteCases@Which@
# "F", = + HFirst ê@ L,# "B", = − HFirst ê@ L;,# "Ω", = .880, 0, 1<, 80, 1, 0<, 8−1, 0, 0<<;,# "", = .880, 0, −1<, 80, 1, 0<, 81, 0, 0<<;,# " ", = .880, −1, 0<, 81, 0, 0<, 80, 0, 1<<;,# " ", = .880, 1, 0<, 8−1, 0, 0<, 80, 0, 1<<;,# "", = .881, 0, 0<, 80, 0, 1<, 80, −1, 0<<;,# "∆", = .881, 0, 0<, 80, 0, −1<, 80, 1, 0<<;,True, NullD & ê@ Flatten@Nest@# ê. recursion &,Characters@axiomD, nDD, NullD, 80, 0, 0<DD
159
今,n = 1とすると,
gnC3D = Line@nestedCurve3D@1DDLine@880, 0, 0<, 80, 0, 1<, 80, 1, 1<,80, 1, 0<, 81, 1, 0<, 81, 1, 1<, 81, 0, 1<, 81, 0, 0<<D
のようになり,原点から7回前進していることを示す.図に表すと,
Show@Graphics3D@[email protected], [email protected], gnC3D<D,PlotRange → All, Axes → True,
AxesEdge → 88−1, −1<, 81, −1<, 8−1, −1<<D;
00.25
0.50.75
1 00.250.50.751
00.25
0.5
0.75
1
00.25
0.50 75
となる.最後に,n = 2, 3, 4として描画すると,
Show@GraphicsArray@Block@8$DisplayFunction = Identity<,Show@Graphics3D@[email protected], [email protected],
Line@nestedCurve3D@#DD<D, PlotRange → All,
Axes → True, AxesEdge → 88−1, −1<, 81, −1<, 8−1, −1<<,PlotLabel → "n=" <> ToString@#DD & ê@ 82, 3, 4<DDD;n=2
01
23 0
123
0
1
2
3
01
2
n=3
02
46 0
2460
2
4
6
02
4
n=4
0510
15 051015
05
10
15
0510
となる.これをヒルベルト曲線という.
160