必要なパッケージを import します。
from sympy.abc import *
from sympy import *
# SymPy Plotting Backends (SPB): グラフを描く際に利用
from spb import *
# グラフを SVG で Notebook にインライン表示
%config InlineBackend.figure_formats = ['svg']
フーリエ級数
- 区間 $-\pi < x < \pi$ で定義された関数 $f(x)$ は,それがどんな関数であっても…
- 区間外では,周期 $ 2\pi $ の周期関数とみなして
- 三角関数 $\cos, \ \sin$ の重ね合わせて表すことができる!
つまり,以下のように書けるということ。
$$ f(x) = \frac{a_0}{2} + \sum_{n=1}^{\infty} \bigl( a_n \cos n x + b_n \sin nx \bigr) $$
ここで,フーリエ係数 $a_n, b_n$ は
$$a_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \cos nx \, dx $$$$b_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \sin nx \, dx $$
例題
区間 $[-\pi: \pi]$ で定義された関数 $f(x) = x^2$ のフーリエ級数展開。
周期 $2 \pi$ の周期関数として,
- $[-3\pi:-\pi]$ では $f(x) = (x+2 \pi)^2$…
- $[-\pi:\pi]$ では $f(x) = x^2$,
- $[\pi:3\pi]$ では $f(x) = (x-2 \pi)^2$…
のようにすればいい。
まず,1周期分の $f_0(x) \equiv x^2$ を定義して,$[-\pi:\pi]$ の区間でグラフを描く。
def f0(x):
return x**2
plot(f0(x), (x, -pi, pi));
$[-\pi:\pi]$ の区間外では,周期 $2 \pi$ の周期関数として,
- $[-3\pi:-\pi]$ では $f(x) = f_0(x+2 \pi)$…
- $[-\pi:\pi]$ では $f(x) = f_0(x)$,
- $[\pi:3\pi]$ では $f(x) = f_0(x-2 \pi)$…
をグラフに描く。
plot((f0(x+2*pi), (x, -3*pi, -pi)),
(f0(x), (x, -pi, pi)),
(f0(x-2*pi), (x, pi, 3*pi)), legend = False);
周期毎に別の関数を描くのは面倒なので,切り捨てる関数 floor()
を使って周期 $2\pi$ の関数 $f(x)$ を定義します。
def f(x):
return f0(x-2*pi*floor((x+pi)/(2*pi)))
plot(f(x), (x, -3*pi, 3*pi));
少しオプションを設定して描く例:
以下の例では,
- 凡例を設定し,
- 横軸の表示範囲を設定し,
- 縦軸の表示範囲を設定し,
- $x$ 軸のフォーマットを $\pi$ の倍数になるように設定しています。
fpi = float(pi)
p = plot(f(x), (x, -3*pi, 3*pi),
"$f(x)$", legend=True,
xlim = (-3*pi, 3*pi),
ylim = (-2, 12), show = False)
ax = p.ax
ax.set_xticks([(i*fpi) for i in range(-3,4)])
xlab = ["-3 $\pi$", "-2 $\pi$", "-$\pi$", "0",
"$\pi$", "$2 \pi$", "3 $\pi$"]
ax.set_xticklabels(xlab);
フーリエ級数は,以下のように書けました。
$$ f(x) = \frac{a_0}{2} + \sum_{n=1}^{\infty} \bigl( a_n \cos n x + b_n \sin nx \bigr) $$
ここで,フーリエ係数 $a_n, b_n$ は
$$a_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \cos nx \, dx $$$$b_n = \frac{1}{\pi} \int_{-\pi}^{\pi} f(x) \sin nx \, dx $$
SymPy にはフーリエ級数を扱う関数がありますが,簡単なので,積分と和をとる演算を以下のように定義してしまいます。
# フーリエ係数の定義,フーリエ級数の定義。
n = Symbol('n', integer = True, positive = True)
def a(n):
return 1/pi*integrate(f0(x)*cos(n*x), (x, -pi, pi))
def b(n):
return 1/pi*integrate(f0(x)*sin(n*x), (x, -pi, pi))
def Fourier(n, x):
var('i')
return a(0)/2 + summation(
a(i)*cos(i*x) + b(i)*sin(i*x), (i, 1, n))
ちなみに,上のセルの定義で使われている関数 integrate()
は積分を実行し, summation()
は和をとり,説明は以下の通りです。
$\displaystyle \int_a^b f(x)\, dx = $ integrate(f(x), x, a, b);
$\displaystyle \sum_{i = 1}^n a_i = $ summation(a(i), (i, 1, n));
$f(x) = x^2$ は偶関数なので,奇関数部分のフーリエ係数である $b_n$ はゼロのはず。実際に調べてみると…
for i in range(1, 5):
print("b(%d) = " % i,b(i))
# 一般の n については...
b(n)
a(0)
for i in range(1, 5):
print("a(%d) = " % i,a(i))
# 一般の n については...
a(n)
f1 = Fourier(1, x)
f1
f2 = Fourier(2, x)
f2
f3 = Fourier(3, x)
f3
f4 = Fourier(4, x)
f4
それぞれの次数までのフーリエ級数展開の式と,もとの周期関数 $f(x)$ を重ねてグラフにします。
p=plot(
(f(x), (x, -3*pi, 3*pi), "f(x)"),
(f4, (x, -3*pi, 3*pi), "n=4", {"linewidth":0.7}),
(f3, (x, -3*pi, 3*pi), "n=3", {"linewidth":0.7}),
(f2, (x, -3*pi, 3*pi), "n=2", {"linewidth":0.7}),
(f1, (x, -3*pi, 3*pi), "n=1", {"linewidth":0.7}),
xlim = (-3*pi, 3*pi), ylim = (-2, 12),
size=(6,6), show = False
)
ax = p.ax
ax.set_xticks([(i*fpi) for i in range(-3,4)])
xlab = ["-3 $\pi$", "-2 $\pi$", "-$\pi$", "0",
"$\pi$", "$2 \pi$", "3 $\pi$"]
ax.set_xticklabels(xlab);
任意の周期をもつ関数のフーリエ級数展開
周期 $2\pi$ の決め打ちのフーリエ級数ではなく,任意の周期をもつ場合は,一般に周期を $2L$ として…
$$ f(x) = \frac{a_0}{2} + \sum_{n=1}^{\infty} \bigl( a_n \cos \frac{n\pi x}{L} + b_n \sin \frac{n\pi x}{L} \bigr) $$$$a_n = \frac{1}{L} \int_{-L}^{L} f(x) \cos \frac{n\pi x}{L} \, d{x} $$$$b_n = \frac{1}{L} \int_{-L}^{L} f(x) \sin \frac{n\pi x}{L} \, d{x} $$
例題
区間 $[-1:1]$ で定義された関数 $f(x) = x$ が,区間外では周期 $2$ の周期関数であるとして,$n=5$ までのフーリエ級数展開を求める。
上記の式で $L = 1$ とすればよい。
一般に,1区間1周期で $f_0(x)$ として定義された関数を,その区間外で周期 $2L$ の周期関数にするには,上記のような $x_{\rm cyc}$ を使えばよい。
def xcyc(x, L):
return x - 2*L*floor((x+L)/(2*L))
def f0(x):
return x
def f(x):
return f0(xcyc(x, 1))
plot(f(x), (x, -5, 5));
# L = 1 決めうちで
def a(n):
return integrate(f0(x)*cos(n*pi*x), (x, -1, 1))
def b(n):
return integrate(f0(x)*sin(n*pi*x), (x, -1, 1))
def Fourier(n, x):
var('i')
return a(0)/2 + summation(
a(i)*cos(i*pi*x) + b(i)*sin(i*pi*x), (i, 1, n))
$f_0(x) = x$ は奇関数なので,偶関数部分のフーリエ係数である $a_n$ はゼロのはず。実際に調べてみると…
# 任意の n について...
a(n)
b(1)
b(2)
b(3)
b(n)
f2 = Fourier(2, x)
f2
f3 = Fourier(3, x)
f3
f4 = Fourier(4, x)
f4
plot(
(f(x), (x, -5, 5), "f(x)"),
(f4, (x, -5, 5), "n=4", {"linewidth":0.7}),
(f3, (x, -5, 5), "n=3", {"linewidth":0.7}),
(f2, (x, -5, 5), "n=2", {"linewidth":0.7}),
xlim = (-5, 5), ylim = (-1.3, 1.8),
size = (6, 5)
);
フーリエ積分・フーリエ変換
任意の周期 $2L$ をもつ関数の複素フーリエ級数展開を,非周期的現象にまで拡張したものが「フーリエ積分」であり,フーリエ係数の拡張が「フーリエ変換」。
周期性のない関数に対する(連続極限としての)フーリエ積分
$$ f(x) = \frac{1}{2\pi} \int_{-\infty}^{\infty}\ F(k)\ e^{i k x}\ dk, \quad F(k) \equiv \int_{-\infty}^{\infty} f(x) \ e^{-i k x} \ dx $$
例題
以下の周期性のない関数 $f(x)$ のフーリエ変換 $F(k)$ を求めよ。
$$ f(x) = \begin{cases}
\frac{1}{a} & (|x| \leq \frac{a}{2}) \\
0 & (|x|> \frac{a}{2})
\end{cases}$$
次に,以下の極限を求めよ。$$ \lim_{a \rightarrow 0} F(k) =?$$
もし $\displaystyle |x| \leq \frac{a}{2}$ なら $\displaystyle \frac{1}{a}$ を返し,それ以外なら $0$ を返す関数 $f(x)$ の定義。
if 文を使いそうになるが,この場合は Piecewise()
を使うらしい。
def f(a, x):
return Piecewise((1/a, abs(x) <= a/2),
(0, abs(x) > a/2))
plot(f(2, x), (x, -5, 5), ylim = (-0.5, 1));
$$ F(k) = \int_{-\infty}^{\infty} f(x) \ e^{-i k x} \ dx = \int_{-\frac{a}{2}}^{\frac{a}{2}} \frac{1}{a}\ e^{-i k x} \ dx$$ということを使って,フーリエ変換の無限積分を有限区間の積分にします。
var('a k', nonzero = True)
F = integrate(1/a*exp(-I*k*x), (x, -a/2, a/2))
F.simplify()
Eq(Limit(F.simplify(), a, 0),
Limit(F.simplify(), a, 0).doit())
算数の問題として,
$$ \lim_{a \rightarrow 0} F(k) = \lim_{a \rightarrow 0} \frac{\sin\frac{ak}{2}}{\frac{ak}{2}} = 1$$
となることはわかった。では,フーリエ変換 $F(k)$ が $1$ となる $f(x)$ とはどのような関数であろうか。
以下のグラフから推察されるように,$a$ の値を小さくしていくと $f(x)$ は $x = 0$ の近くでのみ値をもつ関数になる。
$\displaystyle \int_{-\infty}^{\infty} f(x)\, dx =\int_{-\frac{a}{2}}^{\frac{a}{2}} \frac{1}{a} \,dx = 1$ という面積を保ちながら,$a \rightarrow 0$ で幅 $a$ がどんどん狭くなっていくため,高さ $\displaystyle \frac{1}{a}$ がどんどん高くなっていきます。
plot((f(0.1, x), (x, -5, 5), "a=0.1"),
(f(0.2, x), (x, -5, 5), "a=0.2"),
(f(0.5, x), (x, -5, 5), "a=0.5"),
(f(2, x), (x, -5, 5), "a=2"), legend = True);
この関数 $f(x)$ の $a \rightarrow 0$ の極限は,「デルタ関数」$\delta(x)$ と呼ばれます。定義は,(フーリエ変換 $F(k)$ が $1$ であるフーリエ積分であるから)
$$\delta(x) \equiv \frac{1}{2\pi} \int_{-\infty}^{\infty} e^{i k x}\ dk$$
デルタ関数は以下のような性質を持ちます。
$$\delta(x) = 0, \ \mbox{if $x \neq 0$}$$$$\int_{-\infty}^{\infty} g(x) \delta(x – c)\, dx = g(a)$$
特に,
$$\int_{-\infty}^{\infty} \delta(x)\, dx = 1$$
参考:fourier_series()
関数を使う場合
SymPy にはフーリエ級数展開する関数があります。これを使う例。
例題
区間 $[-\pi: \pi]$ で定義された関数 $f(x) = x^2$ のフーリエ級数展開。
まず,1周期分の関数 $f_0(x) = x^2$ を定義し…
f0 = x**2
s = fourier_series(f0, (x, -pi, pi))
$n=2$ までのフーリエ級数展開は n = 3
として…
n = 2
ではなく,n = 3
とする理由は $a_0, a_1, a_2$ と3個あるから?… というよりは,truncate(n = 3)
の n = 3
がゼロではない最初の3項を表示するという意味があるから。
f2 = s.truncate(n = 3)
f2
同様にして…
f3 = s.truncate(n = 4)
f3
f4 = s.truncate(n = 5)
f4
例題
区間 $[-1:1]$ で定義された関数 $f(x) = x$ が,区間外では周期 $2$ の周期関数であるとして,$n=5$ までのフーリエ級数展開を求める。
f0 = x
s = fourier_series(f0, (x, -1, 1))
# この場合は n = 5 で 5次まで表示
f5 = s.truncate(n = 5)
f5