2026年5月21日 星期四

皮薩諾數字圈 python 程式

 




"""

皮薩諾週期 60 圓圈圖

Fibonacci mod 10 的週期為 60,將 60 個個位數排列於圓周上。

需要:matplotlib(pip install matplotlib)

"""


import math

import matplotlib.pyplot as plt

import matplotlib.patches as patches


# ── 1. 產生皮薩諾數列(Fibonacci mod 10,週期 = 60)──────────────────────────

def pisano60():

    seq = [0, 1]

    for i in range(2, 60):

        seq.append((seq[-1] + seq[-2]) % 10)

    return seq


seq = pisano60()

N = 60


# ── 2. 角度工具(從正上方 -π/2 開始,順時針)───────────────────────────────

def angle(i):

    return (i / N) * 2 * math.pi - math.pi / 2


def xy(r, i):

    a = angle(i)

    return math.cos(a) * r, math.sin(a) * r


# ── 3. 半徑設定 ───────────────────────────────────────────────────────────────

R_outer  = 1.00   # 外圓

R_dot    = 0.88   # 點的位置

R_label  = 0.97   # 數字標籤

R_inner  = 0.76   # 弦線端點 / 虛線圓

R_idx    = 0.68   # 位置索引


# ── 4. 畫布 ───────────────────────────────────────────────────────────────────

fig, ax = plt.subplots(figsize=(8, 8), facecolor='#f5f2eb')

ax.set_aspect('equal')

ax.axis('off')

ax.set_xlim(-1.15, 1.15)

ax.set_ylim(-1.15, 1.15)


COLOR = '#1a1a1a'


# ── 5. 外圓與虛線內圓 ─────────────────────────────────────────────────────────

for r, lw, ls in [(R_outer, 0.8, '-'), (R_inner, 0.4, (0, (4, 4)))]:

    circle = plt.Circle((0, 0), r, color=COLOR, fill=False,

                         linewidth=lw, linestyle=ls)

    ax.add_patch(circle)


# 圓心點

ax.plot(0, 0, 'o', color=COLOR, markersize=3)


# ── 6. 刻度線 ─────────────────────────────────────────────────────────────────

for i in range(N):

    major = (i % 5 == 0)

    r_in  = R_inner - 0.01 if major else R_inner + 0.03

    r_out = R_outer + 0.04

    x1, y1 = xy(r_in,  i)

    x2, y2 = xy(r_out, i)

    ax.plot([x1, x2], [y1, y2],

            color=COLOR,

            linewidth=1.2 if major else 0.4,

            alpha=0.6  if major else 0.25)


# ── 7. 弦線(i → (i + seq[i]) % 60)─────────────────────────────────────────

for i in range(N):

    d = seq[i]

    if d == 0:

        continue          # 跳過自環

    j = (i + d) % N

    x1, y1 = xy(R_inner, i)

    x2, y2 = xy(R_inner, j)

    ax.plot([x1, x2], [y1, y2],

            color=COLOR, linewidth=0.35, alpha=0.12)


# ── 8. 點與數字標籤 ───────────────────────────────────────────────────────────

for i in range(N):

    d = seq[i]

    xd, yd = xy(R_dot, i)

    xl, yl = xy(R_label, i)


    # 點

    size   = 5 if d == 0 else 3

    alpha  = 1.0 if d == 0 else 0.65

    ax.plot(xd, yd, 'o', color=COLOR, markersize=size, alpha=alpha)


    # 數字(個位數)

    ax.text(xl, yl, str(d),

            ha='center', va='center',

            fontsize=7.5,

            fontweight='bold' if d == 0 else 'normal',

            color=COLOR,

            alpha=1.0 if d == 0 else 0.85)


# ── 9. 位置索引(每 5 格,內圈小字)──────────────────────────────────────────

for i in range(0, N, 5):

    xi, yi = xy(R_idx, i)

    ax.text(xi, yi, str(i),

            ha='center', va='center',

            fontsize=5.5, color=COLOR, alpha=0.35)


# ── 10. 標題 ──────────────────────────────────────────────────────────────────

ax.set_title('皮薩諾週期 — 60', fontsize=11,

             color=COLOR, fontfamily='serif',

             letter_spacing=3 if hasattr(ax.title, 'set_letterspacing') else None,

             pad=12)


plt.tight_layout()

plt.savefig('pisano60.png', dpi=180, bbox_inches='tight',

            facecolor=fig.get_facecolor())

plt.show()

print("已儲存 pisano60.png")