はじめに
美しいフィボナッチ螺旋を自分で描いてみたかったのです。
螺旋は、原点Oを始点としてまず右下に伸び、反時計回りに描くようにしました。
環境
- Python 3.9.6
- matplotlib 3.4.2
- MacOS 11.5.2
手法
項ごとに、以下のように処理し、あたかも1本のグラフであるかのように見せています。
- 90度の弧を描く
- 各項の値を弧の半径とする
- 弧を90度ずつ回転させる
- 弧の中心をオフセットする
1本のグラフとして、x・yの変域を細かく設定して描く方法もあるのでしょうが、今回はその方法は採っていません。
コード
モジュールのインポートとグラフ周りの設定
詳細は割愛。
グラフの範囲は縦横-25から25にしています。draw_areaで定義しています。
# 使用するモジュールのインポート
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
# グラフの設定
fig = plt.figure(figsize=(15, 15), dpi=72, tight_layout=True)
fig.suptitle('Fibonacci', fontsize=30)
ax = fig.add_subplot(111)
draw_area = 25
# グラフの目盛り・補助線の設定
ax.set_xlim(-draw_area, draw_area)
ax.set_ylim(-draw_area, draw_area)
ax.set_aspect('equal')
ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax.grid(axis='both', color="blue", lw=0.5, ls="--")
フィボナッチ数を生成する関数
もっとも(遅く)素朴なものを使ってます。
def get_fib(n):
if n == 0 or n == 1:
return n
else:
return get_fib(n - 1) + get_fib(n - 2)
弧の中心を求める関数
手法4.で述べた弧の中心のオフセットを行います。
任意の基準点からの絶対位置を採るのではなく、前項からの相対位置を採るようにしています。
中心点を繋いだ軌跡は下図です。
0・1項目は固定値を与えるほかなかろうと。
2項目以降については、移動距離はふた項前の値でよいのですが、xyの両軸を交互に移動させ、かつ軸ごとに移動の向きを切り替える必要があるため、非常に泥臭い処理をしています。
x, y = 0, 0 # 中心の初期値
xb, yb = -1, -1 # 移動の向きを正負で規定
def get_center_pos(n):
global x, y, xb, yb
if n == 0: # (0項目はそもそも軌跡がない)
x, y = 0, 0
elif n == 1: # 1項目は原点Oから右下への弧を描かせるのでx=1、y=0
x, y = 1, 0
elif n % 2 == 1:
if xb == -1:
x -= get_fib(n - 2)
xb = +1
else:
x += get_fib(n - 2)
xb = -1
elif n % 2 == 0:
if yb == -1:
y += get_fib(n - 2)
yb = +1
else:
y -= get_fib(n - 2)
yb = -1
return x, y
弧を描く処理
弧はmatplotlib.patchesのArcで描きます。angleは初期値は90です。
手法の1.〜3.について、以下のように実践しています。
- 90度の弧を描く:theta2とtheta1の差分
- 各項の値を弧の半径とする:widthとheightにget_fib(i) * 2を代入
- 弧を90度ずつ回転させる:angleの値を90ずつ増分
my_angle = 90
for i in range(0, 11): #最終値は任意
my_arc = mpatches.Arc(xy=get_center_pos(i), width=get_fib(i) * 2, height=get_fib(i) * 2, angle=my_angle,
theta1=0, theta2=90,linewidth=5, color="r")
ax.add_patch(my_arc)
my_angle += 90
plt.show()
コード全体
上記の記述を繋げばよいです。
コード全体
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches
fig = plt.figure(figsize=(15, 15), dpi=72, tight_layout=True)
fig.suptitle('Fibonacci', fontsize=30)
ax = fig.add_subplot(111)
draw_area = 25
ax.set_xlim(-draw_area, draw_area)
ax.set_ylim(-draw_area, draw_area)
ax.xaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax.yaxis.set_major_locator(mpl.ticker.MultipleLocator(1))
ax.grid(axis='both', color="blue", lw=0.5, ls="--")
ax.set_aspect('equal')
def get_fib(n):
if n == 0 or n == 1:
return n
else:
return get_fib(n - 1) + get_fib(n - 2)
x, y = 0, 0
xb, yb = -1, -1
def get_center_pos(n):
global x, y, xb, yb
if n == 0:
x, y = 0, 0
elif n == 1:
x, y = 1, 0
elif n % 2 == 1:
if xb == -1:
x -= get_fib(n - 2)
xb = +1
else:
x += get_fib(n - 2)
xb = -1
elif n % 2 == 0:
if yb == -1:
y += get_fib(n - 2)
yb = +1
else:
y -= get_fib(n - 2)
yb = -1
return x, y
my_angle = 90
for i in range(0, 11):
my_arc = mpatches.Arc(xy=get_center_pos(i), width=get_fib(i) * 2, height=get_fib(i) * 2, angle=my_angle,
theta1=0, theta2=90,linewidth=5, color="r")
ax.add_patch(my_arc)
my_angle += 90
plt.show()
実行結果
下図のように、原点Oを始点としたフィボナッチ螺旋が描画されています。
最後に
次は、x・yの変域を細かく設定して1本のグラフとして描く方法を試してみようかと。