POINT
思いついた方法をメモしておきます.- 動的に変数の数が変わる関数を「定義」する方法.
- 動的に変数の数が変わる関数を「積分」する方法.
他にもっと良い方法があるかもしれません.ぜひ教えて下さい!
やりたいこと
例えば\begin{aligned}
f_1(x_{0}, x_{1}, \cdots , x_{N})
=\prod_{i=0}^{N} g_1(x_i)
\end{aligned}
という関数やf_1(x_{0}, x_{1}, \cdots , x_{N})
=\prod_{i=0}^{N} g_1(x_i)
\end{aligned}
\begin{aligned}
f_2(x_{0}, x_{1}, \cdots , x_{N})
=\prod_{i=0}^{N-1} g_2(x_{i}, x_{i+1})
\end{aligned}
という関数を積分したい.f_2(x_{0}, x_{1}, \cdots , x_{N})
=\prod_{i=0}^{N-1} g_2(x_{i}, x_{i+1})
\end{aligned}
さらに,変数の数(関数$g$を掛ける回数)を色々変えて計算したい.このとき,変数の個数ごとに関数を定義するのではなく,1つの関数でやりくりしたい.
例1:1変数関数を掛け合わせる
上の$f_1$について考えましょう.可変長引数をもつ関数
具体例として$g_1(x)=x$の場合を考えます.したがって,$f_1(x_{0}, x_{1}, \cdots , x_{N}) = x_{0} \times x_{1} \times \cdots \times x_{N}$となります.関数は以下のように定義できます:
def g1(x): y = x return y def f1(*args): y = 1 for x in args: y = y * g1(x) return y
もちろん,$g_1$の定義を変えればこのタイプのどんな関数でも表現できます.
上の例では,この関数を利用することで
\begin{aligned}
f_1(1,2,3,\cdots ,N)=N!
\end{aligned}
が計算できます.確認してみましょう:
f_1(1,2,3,\cdots ,N)=N!
\end{aligned}
import numpy as np def g1(x): y = x return y def f1(*args): y = 1 for x in args: y = y * g1(x) return y n = 5 # integer variable = np.arange(1, n+1) # 5! = 120 print(str(n)+'! =', f1(*variable))
積分
$f_1$の積分は\begin{aligned}
I
&=\int_{a_{0}}^{b_{0}}\cdots \int_{a_{N}}^{b_{N}}
f_1(x_{0}, x_{1}, \cdots , x_{N})
\,\mathrm{d}x_{0}\cdots \,\mathrm{d}x_{N} \\
&=\prod_{i=0}^{N} \biggl[ \int_{a_{i}}^{b_{i}} g_1(x_i) \,\mathrm{d}x_{i} \biggr]
\end{aligned}
となります.I
&=\int_{a_{0}}^{b_{0}}\cdots \int_{a_{N}}^{b_{N}}
f_1(x_{0}, x_{1}, \cdots , x_{N})
\,\mathrm{d}x_{0}\cdots \,\mathrm{d}x_{N} \\
&=\prod_{i=0}^{N} \biggl[ \int_{a_{i}}^{b_{i}} g_1(x_i) \,\mathrm{d}x_{i} \biggr]
\end{aligned}
ここで,また$g_1(x)=x$とすると
\begin{aligned}
I&=\prod_{i=0}^{N} \biggl[ \frac{(b_{i})^2 - (a_{i})^2}{2} \biggr]
\end{aligned}
と厳密に計算できます.そこで,次の具体例で「scipy.integrate.nquadによる積分結果」と「厳密解」を比較して一致することを確認しましょう:I&=\prod_{i=0}^{N} \biggl[ \frac{(b_{i})^2 - (a_{i})^2}{2} \biggr]
\end{aligned}
\begin{aligned}
I
&=\int_{0}^{1}\,\mathrm{d}x_{0}
\int_{1}^{2} \,\mathrm{d}x_{1}
\int_{3}^{4}\,\mathrm{d}x_{2}\,
[x_{0} x_{1} x_{2}]
\end{aligned}
I
&=\int_{0}^{1}\,\mathrm{d}x_{0}
\int_{1}^{2} \,\mathrm{d}x_{1}
\int_{3}^{4}\,\mathrm{d}x_{2}\,
[x_{0} x_{1} x_{2}]
\end{aligned}
import numpy as np from scipy import integrate def g1(x): y = x return y def f1(*args): y = 1 for x in args: y = y * g1(x) return y def exact(*args): y = 1 for x in args: y = y * (x[1]**2 - x[0]**2)/2 return y interval_1 = [0, 1] interval_2 = [1, 2] interval_3 = [3, 4] interval = [interval_1, interval_2, interval_3] integral, err = integrate.nquad(f1, interval) # Error= 0.0 print('Error=', integral - exact(*interval))
このように,「scipy.integrate.nquad」の被積分関数として「可変長引数を持つ関数」を指定すれば,渡す積分区間の個数を変えるだけで変数の数($g_1$を掛ける回数)も自動で変えることができます.