第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を使った実装と色付けについて学びます。