2
0

More than 3 years have passed since last update.

フィボナッチ螺旋をPython3+matplotlibで描く

Posted at

はじめに

美しいフィボナッチ螺旋を自分で描いてみたかったのです。
螺旋は、原点Oを始点としてまず右下に伸び、反時計回りに描くようにしました。

環境

  • Python 3.9.6
  • matplotlib 3.4.2
  • MacOS 11.5.2

手法

項ごとに、以下のように処理し、あたかも1本のグラフであるかのように見せています。

  1. 90度の弧を描く
  2. 各項の値を弧の半径とする
  3. 弧を90度ずつ回転させる
  4. 弧の中心をオフセットする

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.で述べた弧の中心のオフセットを行います。
任意の基準点からの絶対位置を採るのではなく、前項からの相対位置を採るようにしています。
中心点を繋いだ軌跡は下図です。
Origin.png

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.について、以下のように実践しています。

  1. 90度の弧を描く:theta2とtheta1の差分
  2. 各項の値を弧の半径とする:widthとheightにget_fib(i) * 2を代入
  3. 弧を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を始点としたフィボナッチ螺旋が描画されています。
Fibonacci.png

最後に

次は、x・yの変域を細かく設定して1本のグラフとして描く方法を試してみようかと。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0