フラクタル模様と数列

# はじめに
フラクタル模様は、複素数平面の各点についてある試行を行い一定の値以上になった回数を用いて描写することができる。今回は、そのことを利用して以下のようなフラクタル模様を等高線プロットを用いてPythonで描写することができるかに挑戦する。

![fractal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/c2d64950-8f3e-0b3d-7902-a89cced92d35.png)


# イメージ

複素数平面には、以下の画像の様に無数の点が等間隔で存在する。

![格子状の点.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/89dd988c-f7d9-58df-04d2-78abcd9a320c.png)

その各点において、$z_1=x+jy$としたとき、

```math
z_{n+1}=z_{n}^2+c
```

（ただし、$c$は複素数）

という数列を回したとき、実数$r$を用いて$|z_n|<r$を満たさなくなるまでの試行回数nを高さとした点をプロットする。 

この点の集合体がフラクタル模様となる。

# プログラム
## フラクタル模様
今回は、各点における試行回数$n$の上限を$p=500$とした。

``` python fractal.py
import numpy as np
import matplotlib.pyplot as plt
import math
import japanize_matplotlib

# 分割数
n=100
# 各点における試行回数
p=500
# 描写範囲
x=np.linspace(-1,1,n)
y=np.linspace(-1,1,n)

#|z_n|<r
r=2
# c 複素数
c=-0.3-0.63j
# 格子状の点を生成
X,Y=np.meshgrid(x,y)
# 高さ方向の初期値
Z=np.zeros((n,n))

# 各点における試行回数の規格化した値を計算
for i in range(n):
  for k in range(n):
    #z_1の定義
    z=X[i][k]+Y[i][k]*1j
    #試行回数のカウントm（初期値0）
    m=0
    #|z|<rかつm<pの間繰り返す
    while abs(z)<r and m<p:
      # z_{n+1}=z_n^2+c　（メインとなる数式）
      z=z**2+c
      # 試行回数のカウントアップ
      m=m+1
    #m/pを高さとする（規格化:0<Z[i][k]<1）
    Z[i][k]=m/p
#等高線図を描写することで、フラクタル模様を可視化
plt.contourf(X,Y,Z)
# カラーバーを表示
plt.colorbar()
plt.savefig("fractal.png")
# グラフの表示
plt.show()
```
これを実行すると以下のようなグラフが出力される。

![fractal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/c2d64950-8f3e-0b3d-7902-a89cced92d35.png)

これをフラクタル模様と呼ぶ。おそらく、試行回数の上限$p$や距離$r$や複素数$c$をなどの条件を変更することで様々な模様を描写することが可能になると考えられる。
## 条件変更
そこで、以下のようなプログラムを描いた。

``` python fractal2.py
import numpy as np
import matplotlib.pyplot as plt
import math
import japanize_matplotlib

# 分割数
n=200
# 各点における試行回数
p=1000
# 描写範囲
x=np.linspace(-2,2,n)
y=np.linspace(-2,2,n)

#|z_n|<r
r=2
# c 複素数
c=-0.3-0.63j
# 格子状の点を生成
X,Y=np.meshgrid(x,y)
# 高さ方向の初期値
Z=np.zeros((n,n))

# 各点における試行回数の規格化した値を計算
for i in range(n):
  for k in range(n):
    #z_1の定義
    z=X[i][k]+Y[i][k]*1j
    #試行回数のカウントm（初期値0）
    m=0
    #|z|<rかつm<pの間繰り返す
    while abs(z)<r and m<p:
      # z_{n+1}=z_n^2+c　（メインとなる数式）
      z=z**2+c
      # 試行回数のカウントアップ
      m=m+1
    #m/pを高さとする（規格化:0<Z[i][k]<1）
    Z[i][k]=m/p
#等高線図を描写することで、フラクタル模様を可視化
plt.contourf(X,Y,Z)
# カラーバーを表示
plt.colorbar()
plt.savefig("fractal.png")
# グラフの表示
plt.show()
```

![fractal1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/69cd7d9d-1afd-0820-32e5-75a2e83c31ab.png)

このように、描写範囲を大きくして計算精度を細かくすれば、より美しい模様を描写することができる。

## マンデブロ模様
最後に、$c$を自分自身、つまり$c=z_0=x_0+jy_0$としたときの模様について考える。

``` python fractal3.py
import numpy as np
import matplotlib.pyplot as plt
import math
import japanize_matplotlib

# 分割数
n=200
# 各点における試行回数
p=1000
# 描写範囲
x=np.linspace(-2,2,n)
y=np.linspace(-2,2,n)

#|z_n|<r
r=2
# c 複素数
c=-0.3-0.63j
# 格子状の点を生成
X,Y=np.meshgrid(x,y)
# 高さ方向の初期値
Z=np.zeros((n,n))

# 各点における試行回数の規格化した値を計算
for i in range(n):
  for k in range(n):
    #z_1の定義
    z=X[i][k]+Y[i][k]*1j
    z0=z
    #試行回数のカウントm（初期値0）
    m=0
    #|z|<rかつm<pの間繰り返す
    while abs(z)<r and m<p:
      # z_{n+1}=z_n^2+c　（メインとなる数式）
      z=z**2+z0
      # 試行回数のカウントアップ
      m=m+1
    #m/pを高さとする（規格化:0<Z[i][k]<1）
    Z[i][k]=m/p
#等高線図を描写することで、フラクタル模様を可視化
plt.contourf(X,Y,Z)
# カラーバーを表示
plt.colorbar()
plt.savefig("fractal.png")
# グラフの表示
plt.show()
```

![fractal.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1686019/5e513445-b3f7-0100-fcf7-fe7dc1bb8e2e.png)


このような模様をマンデブロ模様という。

# まとめ
今回は、複素数と数列の融合問題としてフラクタル模様を扱った。フラクタル模様を数列から描写するためには、膨大な計算量を必要とする。そこでPythonを用いることで描写することができるかを試みた。結果上手く描写することができた。また、様々な条件を変えることで、より美しい模様を描写したり、マンデブロ模様を描くことにも成功した。

# 参考文献

1.主にお世話になったサイト（条件、アルゴリズムなど）

https://sites.google.com/site/cinderellajapan/shindererarekucha-1/manderuburo

2.プログラムの書き方で参考にしたサイト

https://t3840_lecture_notes.gitlab.io/2023_spring_3rd_computer_simulation/04_fractal/04_fractal/