第1章:フラクタルの数学的基礎
1.1 フラクタルとは何か
1.1.1 自己相似性の発見
1975年、数学者ブノワ・マンデルブロは「フラクタル」という言葉を造語しました。ラテン語の "fractus"(壊れた、不規則な)に由来するこの言葉は、自然界に溢れる複雑な形状を記述するために生まれました。
フラクタルの特徴:
1. 自己相似性(Self-Similarity)
全体の一部を拡大すると、全体と似た形が現れる
┌─────────────────────────────┐
│ ▲ │
│ ▲ ▲ │ ← シェルピンスキーの三角形
│ ▲ ▲ │
│ ▲ ▲ ▲ ▲ │
│ │
│ 拡大すると... │
│ │
│ ▲ │
│ ▲ ▲ ← 同じパターン │
│ ▲ ▲ │
└─────────────────────────────┘
2. 無限の複雑さ
どこまで拡大しても新しい構造が現れ続ける
3. 非整数次元(フラクタル次元)
1次元(線)と2次元(面)の間の次元を持つ
自然界のフラクタル:
- 海岸線: どこまで細かく測っても長さが増える
- 雪の結晶: 六角形の中に六角形が繰り返し
- 血管: 大動脈から毛細血管まで分岐パターン
- 雲: 拡大しても雲らしさが維持される
- ブロッコリー: 房を拡大すると同じ形
1.1.2 フラクタルの歴史
フラクタル数学の年表:
1872年 カール・ワイエルシュトラス
├── 連続だが微分不可能な関数を発見
└── 「病的な」曲線の研究開始
1883年 ゲオルク・カントール
├── カントール集合を構築
└── 無限に多くの点を含むが長さ0
1904年 ヘルゲ・フォン・コッホ
├── コッホ雪片を発明
└── 有限面積で無限の周囲長
1915年 ヴァツワフ・シェルピンスキー
└── シェルピンスキーの三角形とカーペット
1918年 ガストン・ジュリア
├── 複素力学系の研究
└── ジュリア集合の発見
1975年 ブノワ・マンデルブロ
├── 「フラクタル」の命名
└── マンデルブロ集合の可視化
1980年代 コンピュータグラフィックス
└── フラクタルが芸術と科学の両方で活用
1.1.3 なぜフラクタルを学ぶのか
fract-olプロジェクトで学べること:
数学的概念:
├── 複素数と複素平面
├── 反復関数と収束・発散
├── 無限と極限の直感的理解
└── カオス理論の入り口
プログラミングスキル:
├── 数値計算の最適化
├── カラーマッピング
├── ズームとパン操作
├── MiniLibXグラフィックス
└── パフォーマンスチューニング
クリエイティブ思考:
├── 数式と美の関係
├── 無限の探索
└── パラメータによる変化の観察
1.2 複素数の世界
1.2.1 虚数の発明
16世紀、イタリアの数学者たちは3次方程式を解く過程で、負の数の平方根という「ありえない」数に遭遇しました。
複素数の誕生:
問題: x² = -1 の解は?
実数の世界では:
x² = 1 → x = ±1
x² = 4 → x = ±2
x² = 0 → x = 0
x² = -1 → 解なし?
虚数単位 i の導入:
i² = -1
i = √(-1)
これにより:
x² = -1 → x = ±i
x² = -4 → x = ±2i
x² = -9 → x = ±3i
歴史的名称:
虚数 (imaginary number) - デカルトによる命名
「想像上の数」という意味だったが、
実は非常に「実用的な」数
1.2.2 複素数の定義
複素数 z = a + bi
a: 実部 (Real part) - Re(z)
b: 虚部 (Imaginary part) - Im(z)
i: 虚数単位 (i² = -1)
例:
3 + 4i: 実部 = 3, 虚部 = 4
-2 + i: 実部 = -2, 虚部 = 1
5: 実部 = 5, 虚部 = 0(純実数)
2i: 実部 = 0, 虚部 = 2(純虚数)
1.2.3 複素平面
複素数を2次元平面上の点として表現できます。
複素平面(ガウス平面):
Im (虚軸)
↑
3 │ ・(3+3i)
│ ╱
2 │ ╱
│ ╱
1 │ ・(1+i)
│ ╱
──────┼───────────→ Re (実軸)
-2 │ 1 2 3
│
-1 │
│ ・(2-i)
-2 │
複素数 z = a + bi は
点 (a, b) として表現される
極形式:
z = r(cos θ + i sin θ)
= r・e^(iθ) (オイラーの公式)
r = |z| = √(a² + b²) (絶対値/大きさ)
θ = arg(z) = atan2(b, a) (偏角)
1.2.4 複素数の演算
加算:
(a + bi) + (c + di) = (a+c) + (b+d)i
幾何学的意味: ベクトルの加算
Im
│ ・z₁+z₂
│ ╱╲
│ ╱ ╲
│ ╱ ・z₂
│╱ ╱
─────●────●────→ Re
z₁
乗算:
(a + bi)(c + di) = ac + adi + bci + bdi²
= (ac - bd) + (ad + bc)i
幾何学的意味: 大きさの積、偏角の和
|z₁ × z₂| = |z₁| × |z₂|
arg(z₁ × z₂) = arg(z₁) + arg(z₂)
二乗:
z² = (a + bi)²
= a² + 2abi + (bi)²
= a² + 2abi - b²
= (a² - b²) + 2abi
これがフラクタル計算の核心!
絶対値:
|z| = |a + bi| = √(a² + b²)
ピタゴラスの定理で計算
1.3 反復関数と力学系
1.3.1 反復とは
同じ操作を繰り返し適用することを「反復」と呼びます。
反復の例:
関数 f(x) = x² を反復:
初期値 x₀ = 2 の場合:
x₁ = f(x₀) = 2² = 4
x₂ = f(x₁) = 4² = 16
x₃ = f(x₂) = 16² = 256
x₄ = f(x₃) = 256² = 65536
...
→ 無限大に発散!
初期値 x₀ = 0.5 の場合:
x₁ = f(x₀) = 0.5² = 0.25
x₂ = f(x₁) = 0.25² = 0.0625
x₃ = f(x₂) = 0.0625² = 0.00390625
...
→ 0に収束!
初期値 x₀ = 1 の場合:
x₁ = f(x₀) = 1² = 1
x₂ = f(x₁) = 1² = 1
...
→ 不変(固定点)
1.3.2 マンデルブロ集合の定義
マンデルブロ集合は、複素数の反復によって定義されます。
マンデルブロ集合:
反復式:
z_{n+1} = z_n² + c
初期条件:
z₀ = 0
定義:
複素数 c がマンデルブロ集合に属する
⟺ この反復で z が無限大に発散しない
判定方法:
|z_n| > 2 となったら発散確定
(一度 |z| > 2 を超えると、必ず発散する)
アルゴリズム:
for each c in 複素平面:
z = 0
for n = 0 to max_iterations:
z = z² + c
if |z| > 2:
c は集合外、色 = n に基づく
break
if n == max_iterations:
c は集合内(黒で表示)
計算例:
c = 0 の場合:
z₀ = 0
z₁ = 0² + 0 = 0
z₂ = 0² + 0 = 0
→ 収束(集合内)
c = 1 の場合:
z₀ = 0
z₁ = 0² + 1 = 1
z₂ = 1² + 1 = 2
z₃ = 2² + 1 = 5
z₄ = 5² + 1 = 26
→ 発散(集合外)
c = -1 の場合:
z₀ = 0
z₁ = 0² + (-1) = -1
z₂ = (-1)² + (-1) = 0
z₃ = 0² + (-1) = -1
z₄ = (-1)² + (-1) = 0
→ 周期的に振動(収束、集合内)
c = i の場合:
z₀ = 0
z₁ = 0² + i = i
z₂ = i² + i = -1 + i
z₃ = (-1+i)² + i = -i
z₄ = (-i)² + i = -1 + i
→ 周期的(収束、集合内)
1.3.3 ジュリア集合の定義
ジュリア集合は、マンデルブロ集合と密接に関連しています。
ジュリア集合:
反復式(マンデルブロと同じ):
z_{n+1} = z_n² + c
違い:
マンデルブロ: z₀ = 0 を固定、c を変化
ジュリア: c を固定、z₀ を変化
定義:
初期値 z₀ がジュリア集合に属する
⟺ この反復で z が無限大に発散しない
重要な関係:
c がマンデルブロ集合内 → ジュリア集合は連結(一塊)
c がマンデルブロ集合外 → ジュリア集合は非連結(塵状)
美しいジュリア集合のパラメータ例:
c = -0.7 + 0.27015i
→ 「フロスト」パターン
c = -0.4 + 0.6i
→ 「ウサギ」パターン
c = 0.285 + 0.01i
→ 「渦巻き」パターン
c = -0.8 + 0.156i
→ 「海馬」パターン
c = -0.74543 + 0.11301i
→ 複雑な螺旋パターン
1.3.4 バーニングシップ・フラクタル
バーニングシップ(燃える船):
反復式:
z_{n+1} = (|Re(z_n)| + i|Im(z_n)|)² + c
違い:
通常: z² + c
バーニングシップ: 各成分の絶対値を取ってから二乗
視覚的特徴:
- 燃えている船のような形状
- 軸対称ではなく非対称
- 独特の「炎」のような構造
計算の詳細:
z = a + bi とすると:
|Re(z)| = |a|
|Im(z)| = |b|
(|a| + i|b|)² = a² - b² + 2|a||b|i
1.4 収束と発散の判定
1.4.1 脱出半径
脱出半径(Escape Radius):
定理: |z_n| > 2 となったら、以降は発散する
証明(概略):
|z_{n+1}| = |z_n² + c|
≥ |z_n|² - |c| (三角不等式)
|z_n| > 2 かつ |c| ≤ 2 のとき:
|z_{n+1}| ≥ |z_n|² - 2
> |z_n| × (|z_n| - 2/|z_n|)
> |z_n| × (|z_n| - 1)
> |z_n|
→ |z_n| は単調増加で発散
実装での扱い:
閾値 = 4(|z|² で比較、sqrt を省略)
if (zr*zr + zi*zi > 4):
発散確定
1.4.2 反復回数と精度
反復回数のトレードオフ:
少ない反復(例: 100回):
✅ 高速
❌ 境界が粗い
❌ 細部が見えない
多い反復(例: 10000回):
❌ 低速
✅ 境界が滑らか
✅ 深いズームで詳細が見える
推奨:
初期表示: 100〜500回
ズーム時: 動的に増加
max_iterations ∝ log(zoom_level)
1.4.3 スムージング
反復回数をそのまま色にすると、等高線のような縞模様になります。
スムージング(連続的な色付け):
問題:
n回で発散 → 色がn
→ 隣接ピクセルで n と n+1 の急な変化
解決策: 分数反復回数
fractional_iteration = n + 1 - log(log(|z_n|)) / log(2)
原理:
|z_n| が閾値に達した時点での「超過量」を考慮
大きく閾値を超えた → より「発散寄り」
ぎりぎり閾値を超えた → より「収束寄り」
実装:
if (zr*zr + zi*zi > 4):
log_zn = log(zr*zr + zi*zi) / 2 // log(|z_n|)
nu = log(log_zn / log(2)) / log(2)
smooth_n = n + 1 - nu
return smooth_n
1.5 フラクタル次元
1.5.1 次元とは何か
通常の次元:
0次元: 点
1次元: 線(長さ)
2次元: 面(面積)
3次元: 立体(体積)
スケーリングとの関係:
1次元の線を2倍に拡大:
長さ = 2¹ = 2 倍
2次元の正方形を2倍に拡大:
面積 = 2² = 4 倍
3次元の立方体を2倍に拡大:
体積 = 2³ = 8 倍
一般に: スケール s 倍 → 量は s^D 倍
D = 次元
1.5.2 フラクタル次元の計算
コッホ曲線の例:
作成手順:
1. 線分を3等分
2. 中央を山型に置き換え
3. 繰り返し
──────── 初期
↓
──╱╲── ステップ1
↓
─╱╲╱╲╱╲─ ステップ2
スケーリング:
1/3 に縮小すると、元の図形の 1/4 のピースが見える
(通常の1次元曲線なら 1/3 のピースのはず)
(1/3)^D = 1/4
D = log(4) / log(3) ≈ 1.26
コッホ曲線の次元は約 1.26
→ 1次元(線)より大きく、2次元(面)より小さい
マンデルブロ集合の境界:
次元 = 2(境界自体が2次元の面を「塗りつぶす」ほど複雑)
1.6 プログラミングへの橋渡し
1.6.1 複素数の実装
/*
* 複素数をCで扱う方法
*/
/* 方法1: 構造体 */
typedef struct s_complex
{
double real;
double imag;
} t_complex;
t_complex complex_add(t_complex a, t_complex b)
{
return ((t_complex){a.real + b.real, a.imag + b.imag});
}
t_complex complex_mult(t_complex a, t_complex b)
{
return ((t_complex){
a.real * b.real - a.imag * b.imag,
a.real * b.imag + a.imag * b.real
});
}
double complex_abs_sq(t_complex z)
{
return (z.real * z.real + z.imag * z.imag);
}
/* 方法2: 変数を直接使用(推奨:高速) */
/*
* zr, zi: z の実部・虚部
* cr, ci: c の実部・虚部
*
* z² + c の計算:
* new_zr = zr*zr - zi*zi + cr
* new_zi = 2*zr*zi + ci
*/
1.6.2 ピクセル座標と複素平面
画面座標から複素座標への変換:
画面: 複素平面:
(0,0)───────→ x Im ↑
│ │
│ │
│ ─────┼─────→ Re
↓ │
y │
変換式:
c_real = (pixel_x - width/2) / zoom + offset_x
c_imag = (pixel_y - height/2) / zoom + offset_y
逆変換(デバッグ用):
pixel_x = (c_real - offset_x) * zoom + width/2
pixel_y = (c_imag - offset_y) * zoom + height/2
/* 座標変換の実装 */
t_complex pixel_to_complex(int px, int py, t_fractol *f)
{
t_complex c;
c.real = (px - WIN_WIDTH / 2.0) / f->zoom + f->offset_x;
c.imag = (py - WIN_HEIGHT / 2.0) / f->zoom + f->offset_y;
return (c);
}
/* より効率的な方法(ループ内で使用) */
void calculate_increments(t_fractol *f)
{
f->x_start = -2.0 / f->zoom + f->offset_x;
f->y_start = -2.0 / f->zoom + f->offset_y;
f->x_inc = 4.0 / (f->zoom * WIN_WIDTH);
f->y_inc = 4.0 / (f->zoom * WIN_HEIGHT);
}
/* 描画ループ */
void render_mandelbrot(t_fractol *f)
{
int px, py;
double cr, ci;
calculate_increments(f);
py = 0;
while (py < WIN_HEIGHT)
{
ci = f->y_start + py * f->y_inc;
px = 0;
while (px < WIN_WIDTH)
{
cr = f->x_start + px * f->x_inc;
/* マンデルブロ計算 */
int color = mandelbrot_point(cr, ci, f->max_iter);
my_mlx_pixel_put(&f->img, px, py, color);
px++;
}
py++;
}
}
1.7 本章のまとめ
フラクタル数学の核心概念:
┌─────────────────────────────────────────────────────────┐
│ フラクタル │
├─────────────────────────────────────────────────────────┤
│ 自己相似性 無限の複雑さ 非整数次元 │
│ │ │ │ │
│ └──────────────────┼───────────────────┘ │
│ │ │
├─────────────────────────┼──────────────────────────────┤
│ 反復関数系 z_{n+1} = f(z_n) │
├─────────────────────────┼──────────────────────────────┤
│ マンデルブロ │ ジュリア │
│ z_{n+1} = z_n² + c │ z_{n+1} = z_n² + c │
│ z₀ = 0, c変化 │ c固定, z₀変化 │
├─────────────────────────┴──────────────────────────────┤
│ 複素数演算 │
│ z = a + bi, z² = (a²-b²) + 2abi │
├─────────────────────────────────────────────────────────┤
│ 収束・発散判定 │
│ |z| > 2 → 発散確定 │
└─────────────────────────────────────────────────────────┘
重要ポイント:
- 複素数: 実部と虚部を持つ数、複素平面で視覚化
- 反復: 同じ操作を繰り返し、収束か発散かを判定
- マンデルブロ: c を変化させ、z₀=0 から反復
- ジュリア: c を固定し、z₀ を変化させて反復
- 脱出判定: |z| > 2 で発散確定
次章では、MiniLibXを使った実装と色付けについて学びます。