\newcommand{\sbr}[1]{\left[#1\right]} \newcommand{\br}[1]{\left(#1\right)} \newcommand{\cbr}[1]{\left\{#1\right\}} \newcommand{\abr}[1]{\left|#1\right|} \newcommand{\exbr}[1]{\left\langle #1 \right\rangle} \newcommand{\nbr}[1]{\left\lVert #1 \right\rVert} \newcommand{\sC}{\mathbb{C}} \newcommand{\sN}{\mathbb{N}} \newcommand{\sR}{\mathbb{R}} \newcommand{\sL}{\mathit{\Lambda}} \newcommand{\sSS}{\mathit{\Omega}} \newcommand{\sFW}{\mathfrak{W}} \newcommand{\sFB}{\mathfrak{B}} \newcommand{\sFF}{\mathfrak{F}} \newcommand{\sFP}{\mathfrak{P}} \newcommand{\Open}[1]{\mathfrak{O}(#1)} \newcommand{\Close}[1]{\mathfrak{A}(#1)} \newcommand{\sComp}[1]{{#1}^{c}} \newcommand{\sIn}[1]{{#1}^{i}} \newcommand{\sAd}[1]{{#1}^{a}} \newcommand{\sOp}[2]{{#1}^{#2}} \newcommand{\rt}{\mathbf{t}} \newcommand{\ReLU}{\mathrm{ReLU}} \newcommand{\rx}{\mathbf{x}} \newcommand{\rf}{\mathbf{f}} \newcommand{\cf}{\mathrm{f}} \newcommand{\rv}{\mathbf{v}} \newcommand{\ry}{\mathbf{y}} \newcommand{\rz}{\mathbf{z}} \newcommand{\card}{\mathrm{card}} \newcommand{\od}{\mathrm{d}} \newcommand{\deriv}[2]{\frac{\partial #1}{\partial #2}}

周期補間の課題

Hiroki Shibata, Tokyo Metropolitan University
Creative Commons License version 4, CC BY, NC

はじめに

周期補間の方法を説明する.これは,周期的な関数の値がいくらか与えられた場合に,与えられていない点の値を補間により予測する方法である.周期的な関数とは,\(f(x) = f(x + T)\)を満たすような\(T\)が存在する関数\(f\)のことを言う.\(T\)はこのときこの関数の周期と呼ばれる.例えば,\(\sin, \cos\)は\(T=2\pi\)の周期関数である.ここではこのような周期関数の補間法を説明する.周期補間法とここでは呼ぶが,いつ誰が考えついたかもわからないくらい,当然のように使われる方法である.一般的にはに示すような周期関数も存在する.

周期関数の例

の関数は,を繰り返したものである.

例に示した周期関数の単位

補間の例を示すに当たり,まず前提を説明する.\(x_i = ih\)で表されている飛び飛びの点(格子点と普通呼ぶ)において,関数の値\(y_i = y(x_i)\)が定義されているとする.添字\(i\)(インデックスとも言う)は\(i=0,1,...,N\)の値を取り,最初と最後の\(y\)の値は等しい,つまり\(y_0 = y_N\)であるとする.もし,\(h=2\pi/N\)とするならば,このような関数として,\(\sin, \cos\)が挙げられる.ここでは,の例を考える.考える周期関数は,の中央の6点(黒5つ,白中ぬき1つ)の点で表される区間線形関数(直線で点を結んだもの)を繰り返したものである.白抜きの点は,原点の黒の点と同じであるとする.よって実際に記録しておくべき値を持つ点の数は\(5\ (=N)\)であり,\(i=N-1\)までが有効な添字の範囲となる.周期は見ての通り,\(T=5h\)である.

補間の説明

普通の補間

簡単のために,まずは,\(0\leq x \lt 5h\)の場合を考えよう.このとき,周期性も\(x \lt 0\)の場合も考える必要はない.\(x\)が属する2つの格子は,原点から\(x\)の場所までに\(h\)の幅がいくつ入るかを数えればよいため,\(x/h\)の値の整数部分を見れば良い.よって, i = \mathrm{int}(x/h) と求められる.補間に用いる左側の点の座標は\(x_i = hi\)であるため,補間の係数は, s = x - hi, t = 1 - s と求められる.これは内分点の公式よりわかる(正確には直線の方程式を考えるが,高校数学までで十分に習得していることを期待しこの説明を省く).

補間に用いる両側の添字を,図より,\(i_1 = i, i_2 = i+1\)と求めようと思うが,\(i=N-1\)のときに,\(i_2 = N\)となり,有効な添字の最大値\(N-1\)を超えてしまうが,\(i=N\)の場所の値はもともと\(i=0\)の場所と同じであるから,これを用いることを考える.これは,次のように添え字を割り算のあまり(剰余演算子\(\%\)による結果)で修正することで実現できる. i_1 = i, i_2 = (i+1)\% N

ここまでで,補間値\(y \)を次の通り求めることができる. y = y_{i_1}t + y_{i_2} s

正の領域における周期補間

次に,\(h5 \leq x\)を含む,\(0\leq x\)の少し一般化した場合を考える.これは,格子点が基準となる範囲,ここでは\(i=0,1,...,4\)を超え,繰り返し終わりなく定義されていると考えれば,わかりが良いと思う.永遠と幅\(h\)で繰り返されていると考えるため,\(x\)の場所までの格子数は,\(\mathrm{int}(x/h)\)であるから,その直前の格子の添字はと同様に, i = \mathrm{int}(x/h) である.補間係数を,対応する\(y_{i}, y_{i+1}\)が用意されているとして,と同様に, s = x - hi, t = 1 - s と求める.このまま,\(y = y_{i_1}t + y_{i_2} s\)として,補間値\(y\)を求めたいところだが,\(y_{i}, y_{i+1}\)の値は用意されていないため,\(y_{i}=y_{i_1}, y_{i+1} = y_{i_2}\)となる\(i_1, i_2 \in\cbr{0,1,2,...,N-1}\)を探し,\(y_{i}, y_{i+1}\)の値を\(y_{i_1}, y_{i_2}\)で代用する.このとき,関数が周期\(T=5h\)で繰り返されていると考えると大変であるため,関数の点を定義する格子常の値が,\(i=0,1,2,...,N-1,0,1,2,...,N-1,...\)のように繰り返し同じ値を参照していることに着目する.このような列は,自然数の列\(j=1,2,...\)に対して, i = j\% N で得られる.先程求めた,\(i\)ももちろんこの規則に従っているため, i_1 = i \% N, i_2 = (i_1+1)\% N となる.どうしても上記の剰余の意味が分かりづらい場合は,実際にいくらか,\(i=0,1,2,3,4,0,1,2,3,4,..., j=1,2,3,4,5,6,7,8....\)のように値を入れて確認してみると良い.

以上で,により周期補間のための添字を求めれば,補間値をで求めることができる.結局,周期性を仮定しない場合との違いは,添字の計算でを用いるか,を用いるかだけである.しかし,の場合を含むため,常に,を用いれば良い.

負の場所を含めた一般の場合

負の場合は,だいぶ難しい.正の領域における周期補間,の場合と同様に,\(i = \mathrm{int}(x/h)\)としたら,これは,原点から数えた\(h\)の幅が入る数を与えるため,この\(i\)を用いると,\(x_i = hi\)と\(x_{i+1}=(i+1)h\)の間に\(x\)が入らなくなる.格子が一つ分正の方向にずれてしまう.ただし,どの場合も,ちょうど一つ分ずれるだけであるから, i = \mathrm{int}(x/h) - 1 と計算すれば良い.で求めた\(i\)を用いれば,\(x \lt 0 \)の場合に \(x_i \lt x \lt x_{i+1}\)が満たされる.この\(i\)を用いて,補間係数をにより求めておく.

次に,正場合と同様に,\(y_{i}=y_{i_1}, y_{i+1} = y_{i_2}\)となる\(i_1, i_2\)を求める.今周期が\(T=hN\)だから,負の方向に添字\(i\)が\(N\)進むごとに,\(i=0\)の場所と同じ値を取ることがわかる.うまく想像ができない場合は,を見よ.可能な限り絵を描いたほうが理解が良いと思う.の真ん中のグラフの格子点に左のグラフのそれぞれの格子点が重ねることを考えれば,格子の添字\(i\)というのは,整数の添字\(j=...,-(N+1), -N,...,-2,-1,0,1,...,N,...,2N,...\)に対して, i = ...N-1, 0, 1,2,..., N-1,0,1,...,N-1,0,1,...,N-1,0,... と並んでいるわけである.\(j\)から上記の\(i\)は, \hat{i} = j\%N, i = (\hat{i} + N)\%N により作ることができる.二度も剰余を使う理由,途中\(N\)を足す理由を説明する.剰余を単に行っただけの\(\hat{i}\)は,\(j/N\)のあまりであるから,負の値を含む.しかし,あまりの定義から,\(\hat{i} = -(N-1), ...,-1,0\)の範囲に収まる.これに\(N\)を足すと,\(\hat{i} + N = 1, 2, ..., N\)となる.\(N\)の場所を\(0\)にして,あとの場所をそのままにしたいため,もう一度\(N\)の剰余を計算し,\(i= (\hat{i}+N)\% N = (j\% N +N)\% N = 1, 2,...,N-1, 0\)を得る.\(j\)に対するこの\(i\)並び方は,ちょうどの並び方に一致している.つまり,負の領域の格子点が基準の領域(図の真ん中の1周期分)の格子点に一致するからこれを用いれば良い.よって, i_1 = (i\%N + N) \% N, i_2 = (i_1+1)\% N を用いる.の場合を含むため,\(x\)が負の場合にはを用い,以降の計算については,常にを用いれば良い.

\(x\)の値が任意の場合における処理を次にまとめる.

  1. \(x \lt 0\)ならば, \(x \geq 0\)ならばで\(i\)を計算
  2. で\(s, t\)の補間係数を計算
  3. により基準の格子の添字を計算
  4. により補間値\(y\)を計算

補間の基礎練習

関数\(f(x)\)の値が,\(h=0.1\)間隔で,格子点\(x_i = hi, i=0,1,...,N-1, N\)上に,\(f_i = f(x_i)\)として,\((f_0, f_1, ...,f_N) = (0.1,0.2, -0.1, 0.2, 0.1)\)と定義されている.\(f_0 = f_N\)であり,\(N=4\)とする.この格子点以外の場所の値は,線形補間により\(f\)の値を定める.また,\(f\)は周期\(hN\)の周期関数であるとする.この\(f\)について,任意の\(x\in\sR\)に対し,\(f(x)\)の値を周期補間により求めるプログラムを実装する.

上記課題の結果を次に示す.ソースコードをperiodic.pyへ示す.正しく周期補間ができていることがわかる.

周期補間による sin, cos ライブラリの課題

\(\sin, \cos\)の値をEuler 法により格子点\(t_i=ih, i=0,1,...,NM, h=2\pi/(NM)\)に対し求め,周期補間により任意の\(x\in \sR\)に対して\(\sin (x), \cos (x)\)の値を与えるプログラムを実装する.この方法ができれば,無理数を取り,多項式で表せない\(\sin, \cos\)の値を高速かつ実用上十分な精度で求める関数が,プログラム開始時にEuler法を計算するだけで得られる.ただし,Euler法は大変多くの格子を用意しなければ精度が保てない一方,補間の精度はEuler法で必要なほど多くは必要なため,Euler法で得られる結果を間引いて,保存する.間引きの間隔は\(M\)であるとする.つまり,補間のための点は\(N\)点のみ記録する.

上記課題の,\(N=32, M=256\)の場合の結果を次に示す.ソースコードをperiodic.pyに示す.補間のために保存してある点は\(N=32\)点だけであるが,もともと\(NM=8192\)分割した\(h\)で計算した精度の比較的高い結果から得られた\(32\)点であるから,これらの点による補間は精度が高いことが期待できる.実際に,見た目には高い精度で補間ができていることがわかる.

\(N=32, M=2\)の場合の結果を次に示す.この場合,オイラー法の精度が足りず,次のように不連続な場所が現れる.ソースコードをperiodic-H.pyへ示す.

\(N=64, M=1\)で,\(i = NM\)の結果を lx, ly へ代入してしまい,かつリストの大きさでlen(lx)あるはlen(ly)の値を設定した結果を次に示す.ここで,リストの大きさは最初と最後が重複しているため,\(N+1\)となっているはずである.結果を見ると,周期の境界付近で,一定の値の場所が確認でき,不連続な補間となっている.ソースコードは periodic-Np1.py に示す.

\(\theta \to |\theta|\)と変換してしまっている場合の結果を次に示す.\(N=32, M=256\)としている.sin が偶関数でないため,絶対値を用いた方法では正しく補間ができないはずで,結果を見てもそれがわかる.periodic-abs.pyへソースコードを示す.

\(\theta \lt 0\)の場合に\(i = \mathrm{int}(\theta/h) -1 \)の処理をしなかった場合の結果を次に示す.\(N=32, M=256\)としている.periodic-nonegative.pyへソースコードを示す.

\(N=256, M=256\)など,補間点\(N\)を増やせば,以下のように見た目には分かりづらい精度の悪さに収まる.しかし,細かい部分では正しく補間でできていないため,実用に用いることはできない.