前々回と前回の授業では、数列のグラフを描画する方法について学びました。今回の授業では、関数のグラフを描画する方法について学びます。
※「関数」という言葉は、数学における関数とプログラミングにおける関数の両方の意味で使うことに注意してください。
関数のグラフの描画においても、数列の場合と同様に次が基本となります。
複数の点の $x$ 座標(横軸の座標)を格納したリストと $y$ 座標(縦軸の座標)を格納したリストを用意する。
matplotlib.pyplotライブラリのplot関数を用いて、点を線分で結んだ折れ線グラフを描画する。
「折れ線グラフ」であることが気になるかもしれませんが、点の個数(リストの要素数)を多くすることであたかも滑らかに見えるグラフを描画することができます。
例として、区間 $[0,1]$ における関数 $y=e^x$ のグラフを描画してみます。
まず、区間 $[0,1]$ を $101$ 個の点に分割したリスト x_list を作成します。
N = 101 #点の個数(グラフが滑らかに見えるほどに多ければ、いくつでもよい)
a = 0 #区間の左端
b = 1 #区間の右端
x_list = [] #x座標(横軸の座標)を格納するリスト
for i in range(N): #iを0からN-1まで変化させて繰り返す
x_list.append(a+(b-a)/(N-1)*i) #i=0のときはa、i=N-1のときはb
print(x_list)
[0.0, 0.01, 0.02, 0.03, 0.04, 0.05, 0.06, 0.07, 0.08, 0.09, 0.1, 0.11, 0.12, 0.13, 0.14, 0.15, 0.16, 0.17, 0.18, 0.19, 0.2, 0.21, 0.22, 0.23, 0.24, 0.25, 0.26, 0.27, 0.28, 0.29, 0.3, 0.31, 0.32, 0.33, 0.34, 0.35000000000000003, 0.36, 0.37, 0.38, 0.39, 0.4, 0.41000000000000003, 0.42, 0.43, 0.44, 0.45, 0.46, 0.47000000000000003, 0.48, 0.49, 0.5, 0.51, 0.52, 0.53, 0.54, 0.55, 0.56, 0.5700000000000001, 0.58, 0.59, 0.6, 0.61, 0.62, 0.63, 0.64, 0.65, 0.66, 0.67, 0.68, 0.6900000000000001, 0.7000000000000001, 0.71, 0.72, 0.73, 0.74, 0.75, 0.76, 0.77, 0.78, 0.79, 0.8, 0.81, 0.8200000000000001, 0.8300000000000001, 0.84, 0.85, 0.86, 0.87, 0.88, 0.89, 0.9, 0.91, 0.92, 0.93, 0.9400000000000001, 0.9500000000000001, 0.96, 0.97, 0.98, 0.99, 1.0]
※小数(浮動小数点数)の計算では誤差が生じるため値がずれている箇所がありますが、グラフの描画に支障はありません。
次に、x_list の各要素に対応する関数の値を計算したリスト y_list を作成します。
import math #eを底とする指数関数expなどの数学関数の使用に必要
y_list = [] #y座標(縦軸の座標)を格納するリスト
for x in x_list:
y_list.append(math.exp(x))
print(y_list)
[1.0, 1.010050167084168, 1.0202013400267558, 1.030454533953517, 1.0408107741923882, 1.0512710963760241, 1.0618365465453596, 1.0725081812542165, 1.0832870676749586, 1.0941742837052104, 1.1051709180756477, 1.1162780704588713, 1.1274968515793757, 1.1388283833246218, 1.1502737988572274, 1.161834242728283, 1.1735108709918103, 1.1853048513203654, 1.1972173631218102, 1.2092495976572515, 1.2214027581601699, 1.2336780599567432, 1.2460767305873808, 1.2586000099294778, 1.2712491503214047, 1.2840254166877414, 1.2969300866657718, 1.3099644507332473, 1.3231298123374369, 1.336427488025472, 1.3498588075760032, 1.3634251141321778, 1.3771277643359572, 1.3909681284637803, 1.4049475905635938, 1.4190675485932573, 1.4333294145603401, 1.4477346146633245, 1.4622845894342245, 1.4769807938826427, 1.4918246976412703, 1.5068177851128537, 1.5219615556186337, 1.5372575235482815, 1.552707218511336, 1.568312185490169, 1.5840739849944818, 1.5999941932173602, 1.6160744021928934, 1.632316219955379, 1.6487212707001282, 1.6652911949458864, 1.6820276496988864, 1.6989323086185506, 1.7160068621848585, 1.7332530178673953, 1.7506725002961012, 1.7682670514337353, 1.7860384307500734, 1.803988415397857, 1.8221188003905089, 1.8404313987816374, 1.858928041846342, 1.8776105792643432, 1.8964808793049515, 1.9155408290138962, 1.9347923344020317, 1.9542373206359396, 1.9738777322304477, 1.9937155332430825, 2.0137527074704766, 2.0339912586467506, 2.0544332106438876, 2.0750806076741224, 2.0959355144943643, 2.117000016612675, 2.1382762204968184, 2.159766253784915, 2.181472265498201, 2.203396426255937, 2.225540928492468, 2.2479079866764717, 2.270499837532406, 2.293318740264183, 2.3163669767810915, 2.3396468519259908, 2.3631606937057947, 2.3869108535242765, 2.4108997064172097, 2.4351296512898744, 2.45960311115695, 2.4843225333848165, 2.509290389936298, 2.534509177617855, 2.5599814183292717, 2.5857096593158464, 2.611696473423118, 2.6379444593541526, 2.664456241929417, 2.691234472349262, 2.718281828459045]
※実は、NumPyというライブラリを利用すると x_list と y_list を簡潔なコードで作成することができますが、ここではプログラムの意味を理解するために敢えてそのようにはしません。NumPyの使い方は、後ほど計算機演習Bの授業で説明します。
最後に、x_list と y_list を用いて折れ線グラフを描画します。
import matplotlib.pyplot as plt #グラフの描画に必要なライブラリ(慣例的にpltという別名を付ける)
plt.plot(x_list,y_list,"-",label="$y=e^x$") #折れ線グラフを描画する 凡例に表示するラベルを指定(数式なのでLaTeXの記法$~$)
plt.grid()
plt.legend() #凡例を表示(各plot関数でlabelを指定する代わりに、ここで引数として文字列のリストで指定してもよい)
plt.show()
区間 $[-\pi,\pi]$ における関数 $y=\sin x$ と $y=\cos x$ のグラフを描画してください。ただし、二つのグラフを一つの図にまとめて表示すること。
#演習1のコード
import math
import matplotlib.pyplot as plt
#ここに、コードを書く
#円周率πはmath.piを使う(関数ではなく変数なので、後ろの括弧は不要)
#sinとcosはmath.sinとmath.cosを使う(関数なので、後ろの括弧と引数が必要)
無限回微分可能な関数 $f:\mathbb{R}\to\mathbb{R}$ に対して、多くの場合に
$$ f(x)=\sum_{k=0}^{\infty}\frac{f^{(k)}(0)}{k!}x^k=f(0)+f^{(1)}(0)x+\frac{f^{(2)}(0)}{2}x^2+\frac{f^{(3)}(0)}{6}x^3+\cdots $$が成り立ちます(ここで、$f^{(k)}$ は $f$ の $k$ 回微分)。これを、関数 $f$ のマクローリン展開(Maclaurin expansion)と呼びます。
※マクローリン展開可能でない(級数が収束しない or 収束しても元の関数と一致しない)ような関数も存在します。
※マクローリン展開は、テイラー展開(Taylor expansion)の特別な場合です。
マクローリン展開における級数の部分和
$$ \sum_{k=0}^{n}\frac{f^{(k)}(0)}{k!}x^k $$を、関数 $f$ の $n$ 次マクローリン多項式(Maclaurin polynomial)と呼びます。
$n$ 次マクローリン多項式は、次数 $n$ が大きくなるほどに元の関数 $f$ の良い近似を与えます。
ネイピア数 $e$ を底とする指数関数 $e^x$ をマクローリン展開すると、次のようになります。
$$ e^x=\sum_{k=0}^{\infty}\frac{1}{k!}x^k=1+x+\frac{1}{2}x^2+\frac{1}{6}x^3+\cdots $$したがって、$e^x$ の $1$ 次マクローリン多項式は
$$ 1+x, $$$2$ 次マクローリン多項式は
$$ 1+x+\frac{1}{2}x^2, $$$3$ 次マクローリン多項式は
$$ 1+x+\frac{1}{2}x^2+\frac{1}{6}x^3 $$となります。
区間 $[-4,4]$ における関数 $y=e^x$、その $1$ 次マクローリン多項式、$2$ 次マクローリン多項式、$3$ 次マクローリン多項式のグラフを描画してください。ただし、四つのグラフを一つの図にまとめて表示すること。
#演習2のコード
import math
import matplotlib.pyplot as plt
#ここに、コードを書く
#plot関数の引数として凡例に表示するラベルを指定する際に、
#そのままだと分数を表すLaTeXのコマンド「\frac」が正常に表示されないため注意
#普通に「/」で分数を表すことはできる
#「label=r"$~$"」とすれば、正常に表示されないLaTeXのコマンドを正しく機能させることができる
区間 $[a,b]$ における関数 $y=e^x$ とその $n$ 次マクローリン多項式のグラフを描画する関数 draw_exp_Maclaurin(a,b,n)
を定義してください。ただし、二つのグラフを一つの図にまとめて表示すること。
$a=-6$、$b=6$、$n=5$ の場合と $a=-6$、$b=6$、$n=7$ の場合について、定義した関数を呼び出してください。
#演習3のコード
import math
import matplotlib.pyplot as plt
def draw_exp_Maclaurin(a,b,n):
#ここに、関数の処理を書く
#階乗の計算には、関数math.factorialを使ってよい
pass #passはエラーを防ぐための「何もしない文」なので、消す
draw_exp_Maclaurin(-6,6,5)
draw_exp_Maclaurin(-6,6,7)
これまできちんと説明をしていませんでしたが、Pythonにおいて変数には使用の有効範囲(スコープ)があります。
関数定義の中で初期化や代入が行われた変数(引数の変数を含む)をローカル変数(local variable)と呼び、ローカル変数の有効範囲はその関数の中だけに限られます。すなわち、関数の外でローカル変数の値を参照することはできず、参照しようとするとエラーになります。
def func1(variable1):
variable2 = 1
return variable1+variable2
print(func1(2))
3
print(variable1)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-8-023ef992a0ee> in <module> ----> 1 print(variable1) NameError: name 'variable1' is not defined
print(variable2)
--------------------------------------------------------------------------- NameError Traceback (most recent call last) <ipython-input-9-32cfdf32b614> in <module> ----> 1 print(variable2) NameError: name 'variable2' is not defined
関数定義の外で初期化や代入が行われた変数をグローバル変数(global variable)と呼びます。関数の中と外でそれぞれ同じ名前のローカル変数とグローバル変数が使用されている場合、それらは完全に別物として扱われます。
def func2():
a = 100
print(a)
a = 0
print(a)
func2()
print(a)
0 100 0
関数の中では、グローバル変数の値を参照することができます。ただし、上の例から分かるように、値の変更をしようとした瞬間に別物のローカル変数が作成されることになる点には注意が必要です。
def func3():
print(a)
a = 0
func3()
0
もし関数の中でグローバル変数の値を変更したい場合には、global文を用いてグローバル変数だと宣言する必要があります。
def func4():
global a
a = 100
print(a)
a = 0
print(a)
func4()
print(a)
0 100 100
演習1~演習3に取り組んでください。