見出し画像

Pythonで高校数学:サイクロイド曲線

 円が直線上を滑ることなく転がるとき,円周上の定点が描く軌跡がサイクロイド曲線である。数学Ⅲの「媒介変数表示の曲線」で学ぶ。これを実際に動かすものを作るのだが,2つの方法が考えられる。

その1 アニメーションにする
その2 スライダを使ってインタラクティブに動かす

 アニメーションは,サイクロイド曲線についての説明をするのに使えるだろう。一方,方程式を求めるために状況を分析するためには,スライダでインタラクティブに動かせるほうがよい。
 また,方程式を求めたあと,実際にプログラミングしてみるのもよい。始めからすべてを作るのは生徒には難しいから,ある程度枠を作っておいて,いくつかを空欄にしてそれを完成させるという学習活動が考えられる。どの程度空欄にするかは,生徒の状況によって異なるだろう。ここでは,実際に何ヶ所かを空欄にしたコードを示すので,ぜひ完成して動かして見てほしい。

その1 アニメーションにする

 アニメーションは matplotlib の animation を使う。

import matplotlib.animation as animation

とし,時刻とともに更新したいことがらを関数(コールバック関数)として定義する。それに対して,

ani = animation.FuncAnimation()

を用いてアニメーション実行する。引数は
fig : plt.figure の戻り値(インスタンス)
func : コールバック関数の関数名
interval:フレームの切り替え間隔:ミリ秒。デフォルトは 200
frames:フレームの枚数:指定しなければ停止するまで動き続ける
repeat:繰り返し指定:Falseにすると繰り返ししない。デフォルトは True

このとき,時刻がコールバック関数に引き渡されるので,コールバック関数側でそれを受け取る。
やっていることはぱらぱらアニメ。少しずつ変えた図をフレームとして何枚も作り,続けて表示している。少し変えるときに,前の画面は消して書き直すので,そのために cla() で画面をクリアする。書き直すので,描画に関することはすべてこのコールバック関数の中に書くことになる。

%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# 動円の半径 1に固定でもよいがあとで図を変更するときのために
r = 1
fig = plt.figure(figsize = (6, 3))
# サイクロイドの媒介変数表示 半径はrで固定
def epix(t):
   return r * (t - np.sin(t))
def epiy(t):
   return 
   
def update(frame):
   th = frame / 20
   plt.cla()
   plt.axis([-1, 7, -1, 3])
   plt.xticks([])
   plt.yticks([])
   plt.axvline(0,color='k')
   plt.axhline(0,color='k')
   #動円を描く
   t = np.linspace(0, , 100)
   x = r * np.cos(t) + r*th
   y = 
   plt.plot(x, y, 'c')
    #軌跡を描く    t = np.linspace(0, th, 200)
   x = epix(t)
   y = epiy(t)
   plt.plot(x,y)
   plt.plot([r*th, epix(th)], [r, epiy(th)],'b', lw=0.5)
   plt.plot([r*th, r*th], [0, r], 'b', lw=0.5)
ani = animation.FuncAnimation(fig, update, 
                             interval=100,
                             frames=126,
                             repeat=False)
#ani.save("cycloid.gif", writer='pillow')
plt.show()

ちょっと煩わしいが(プログラムでは描き終わると止まるが,できたGIFアニメはここでは繰り返し再生されてしまう)ani.save() を有効にしてGIFアニメを作った。

画像2

その2 スライダでインタラクティブに動かす

 アニメーションのかわりにスライダを作る。使うのは matplotlib の widgets  である。これまでに何回か書いてきたから説明は略す。
 アニメーションの場合と異なるのは,コールバック関数では,図を再描画するのではなく,すでに描いた図の引数を変更するということである。したがって,必要な図は先ににすべて描き,それぞれインスタンスを変数で受け取っておく。円の半径やサイクロイド曲線は,最初(回転角0)は実質的には表示されないがデータとしては作っておく。コールバック関数の中で座標を指定すればその座標で再描画される。同じことを2度書くようなものだから,行数は増える。

%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.widgets as wg
# 動円の半径 1に固定でもよいがあとで図を変更するときのために
r = 1
fig = plt.figure(figsize = (6, 3))
ax = plt.axes()
plt.axis([-1, 7, -1, 3])
plt.xticks([])
plt.yticks([])
plt.axhline(0,color='k')
plt.axvline(0,color='k')
# サイクロイドの媒介変数表示 半径はrで固定
def cycx(t):
   return r * (t - np.sin(t))
def cycy(t):
   return 
# 動円,軌跡,半径を初期状態で描く
# 軌跡,半径は見かけ上は表示されないがインスタンスを取得
t = np.linspace(0, , 100)
x = r * np.cos(t)
y = 
g, = plt.plot(x, y, 'c') #軌跡を描く 
t = np.linspace(0, 0, 200)
x = cycx(t)
y = cycy(t)
cyc, = plt.plot(x,y)
r1, = plt.plot([0, 0], [0, 0], 'b', lw=0.5)
r2, = plt.plot([0, 0], [0, 0], 'b', lw=0.5)
# 図を再描画する
def update(th):
   # 円の中心
   cx = r * th
   cy = r
   t = np.linspace(0, , 50)
   x = r * np.cos(t) + cx 
   y = 
   g.set_xdata(x)
   g.set_ydata(y)
   r1.set_xdata([cx, cycx(th)])
   r1.set_ydata([cy, cycy(th)])
   r2.set_xdata([cx, cx])
   r2.set_ydata([0, r])
   t = np.linspace(0, , 200)
   x = cycx(t)
   y = cycy(t)
   cyc.set_xdata(x)
   cyc.set_ydata(y)
# スライダの位置
sx = plt.axes([0.2, 0.05, 0.7, 0.04])
# スライダの設定
slider = wg.Slider(sx, 'angle',-1, 8, valinit=0, valstep=0.1)
slider.on_changed(update)
plt.show()

画像1