ドクログ

Doc's log

ドクログ

3次スプライン補間 (cubic spline interpolation)


  3次スプラインのバリエーションをまとめました。



準備

  はじめに、3次スプラインの基本を整理します。

3次スプラインの定義

  データ点  (x_0,y_0)...(x_N,y_N) を、次の区分多項式  f_j(x) で補間します。 (j=1...N)

 \displaystyle
f_j(x)=a_j(x-x_j)^3+b_j(x-x_j)^2+c_j(x-x_j)+d_j, \;\;\;\;\; (x_{j-1} \le x \le x_j)


ただし、 f_j(x) は以下の条件を満たします。


条件1. 区間右側の端点でデータ点に一致。 (j=1...N)

 \displaystyle
f_j(x_j)=y_j


条件2. 区間左側の端点でデータ点に一致。 (j=1...N)

 \displaystyle
f_j(x_{j-1})=y_{j-1}


条件3. 隣接する区間の境界点で1階微分が一致。 (j=1...N-1)

 \displaystyle
f'_j(x_j)=f'_{j+1}(x_j)


条件4. 隣接する区間の境界点で2階微分が一致。 (j=1...N-1)

 \displaystyle
f''_j(x_j)=f''_{j+1}(x_j)



図1にイメージを示します。

図1 スプライン補間のイメージ

解くべき式の導出

  上の4つの条件式を係数の組  (a_j, b_j, c_j, d_j) について解けば良いですが、これらを直接  (x_j, y_j) の式で表すことは困難です。 そこで先ず  y''_j=f''_j(x_j) を使って、 (a_j, b_j, c_j, d_j) (x_j, y_j, y''_j) の式で表します。その次に、  y''_j が満たすべき式を導きます。


[  d_j の表式 ]

条件1.より

 \displaystyle
f_j(x_j)=d_j=y_j

よって

 \displaystyle
d_j=y_j \tag{1}



[  b_j の表式 ]

 f_j(x) の定義式より

 \displaystyle
\left.f''_j(x_j)=6a_j(x-x_j)+2b_j\,\right|_{x=x_j}=2b_j=y''_j

よって

 \displaystyle
b_j=\frac{y''_j}{2} \tag{2}



[  a_j の表式 ]

条件4.より

 \displaystyle
\left.f''_{j+1}(x_j)=6a_{j+1}(x-x_{j+1})+2b_{j+1}\,\right|_{x=x_j}=6a_{j+1}(-\Delta x_{j+1})+y''_{j+1}=y''_j

よって

 \displaystyle
a_j=\frac{1}{6}\frac{y''_j-y''_{j-1}}{\Delta x_j} \tag{3}


ただし、 \Delta は1つ前の項との差分を表します( \Delta x_j=x_j-x_{j-1})。


[  c_j の表式 ]

条件2.より

 \displaystyle
f_j(x_{j-1})=a_j(x_{j-1}-x_j)^3+b_j(x_{j-1}-x_j)^2+c_j(x_{j-1}-x_j)+d_j=y_{j-1}


すでに求めた係数  (a_j, b_j, d_j) の表式を代入すると

 \displaystyle
\frac{1}{6}\frac{y''_j-y''_{j-1}}{\Delta x_j}(-\Delta x_j)^3+\frac{y''_j}{2}(-\Delta x_j)^2+c_j(-\Delta x_j)+y_j=y_{j-1}

よって

 \displaystyle
c_j=s_{j-1}+\frac{1}{6}\Delta x_j(2y''_j+y''_{j-1}) \tag{4}


ただし、

 \displaystyle
s_j=\frac{\Delta y_{j+1}}{\Delta x_{j+1}}

としました。添字は、後の式の見栄えを良くするため、あえて1つずらしました。


[  y''_j の表式 ]

続けて  y''_j が満たすべき式を導きます。条件3.より

 \displaystyle
f'_j(x_j)=\left.3a_j(x-x_j)^2+2b_j(x-x_j)+c_j\right|_{x=x_j}\\
=\left.3a_{j+1}(x-x_{j+1})^2+2b_{j+1}(x-x_{j+1})+c_{j+1}\right|_{x=x_j}=f'_{j+1}(x_j)

式を整理して

 \displaystyle
3a_{j+1}(\Delta x_{j+1})^2-2b_{j+1}\Delta x_{j+1}+\Delta c_{j+1}=0


係数  (a_j,b_j,c_j) の表式を代入すると

 \displaystyle
\frac{1}{2}\frac{y''_{j+1}-y''_j}{\Delta x_{j+1}}(\Delta x_{j+1})^2-y''_{j+1}\Delta x_{j+1}+\Delta \left(s_j+\frac{1}{6}\Delta x_{j+1}(2y''_{j+1}+y''_j)\right)=0


最終的に、 y''_j の隣接3項間漸化式の形に整理できます。 (j=1...N-1)

 \displaystyle
\Delta x_j y''_{j-1}+2(\Delta x_j+\Delta x_{j+1})y''_j+\Delta x_{j+1} y''_{j+1}= 6\Delta s_j


行列の形で書けば

 \displaystyle
\begin{pmatrix}
\bigcirc & \cdots & \cdots & \cdots & \cdots & \bigcirc \\ \hline
\Delta x_1 & 2(\Delta x_1+\Delta x_2) & \Delta x_2 & & \huge{0} & \\
&\Delta x_2 & \ddots & \ddots & & \\
& & \ddots & \ddots & \Delta x_{N-1} & \\
& \huge{0} & & \Delta x_{N-1} & 2(\Delta x_{N-1}+\Delta x_N) & \Delta x_{N} \\ \hline
\bigcirc & \cdots & \cdots & \cdots & \cdots & \bigcirc \\
\end{pmatrix}
\begin{pmatrix}
y''_0 \\
\vdots \\
y''_N \\
\end{pmatrix}
=
\begin{pmatrix}
\bigcirc \\ \hline
6\Delta s_1 \\
\vdots \\
6\Delta s_{N-1} \\ \hline
\bigcirc \\
\end{pmatrix} \tag{5}


です(横線はただの仕切り線です)。

  式 (5) に示した上下2つの空白 (  \bigcirc ) の行に適当な  y''_j の条件式を入れると、すべての  y''_j が定まり、式 (1)-(4) から係数の組  (a_j,b_j,c_j,d_j) が求まります。このとき加える  y''_j の条件式により、3次スプラインには複数のバリエーションが存在します。

3次スプラインのバリエーションと計算法

  3次スプラインのバリエーションを挙げます。なお、周期的スプライン以外は、左端と右端とで異なるバリエーションを組み合わせることも可能です。

自然スプライン (natural spline)

最端点  x=x_0, x_{N} で2階微分がゼロとなるように  y''_j を決めます。自然スプライン曲線は、区間  [x_0, x_N] の外で直線(=2階微分がゼロ)に滑らかにつながります。

[  y''_j の条件式]

  •  y''_0=y''_N=0


[ 解くべき式 ]

 \displaystyle
\begin{pmatrix}
1 & 0 & \cdots & \cdots & \cdots & 0 \\ \hline
\Delta x_1 & 2(\Delta x_1+\Delta x_2) & \Delta x_2 & & \huge{0} & \\
&\Delta x_2 & \ddots & \ddots & & \\
& & \ddots & \ddots & \Delta x_{N-1} & \\
& \huge{0} & & \Delta x_{N-1} & 2(\Delta x_{N-1}+\Delta x_N) & \Delta x_{N} \\ \hline
0 & \cdots & \cdots & \cdots & 0 & 1 \\
\end{pmatrix}
\begin{pmatrix}
y''_0 \\
\vdots \\
y''_N \\
\end{pmatrix}
=
\begin{pmatrix}
0 \\ \hline
6\Delta s_1 \\
\vdots \\
6\Delta s_{N-1} \\ \hline
0 \\
\end{pmatrix}


  このような、係数行列が3重対角行列で与えられる線形連立方程式は、3重対角行列アルゴリズム(Tri-Diagonal Matrix Algorithm, TDMA)により効率的に解かれます。プログラムの実装は極めて簡単です。

固定スプライン (clamped spline)

  日本語訳が見当たらなかったので、ここでは仮に固定スプラインと呼びます。固定スプラインでは、最端点  x=x_0, x_N での1階微分の値を与えます。

 \displaystyle
f'_1 ( x = x_0 ) = a_1 ( - \Delta x_1 )^2+b_1( - \Delta x_1 ) + c_1 = \alpha \\
f'_N(x=x_N)=c_N=\beta


 \alpha, \beta の値は任意です。  y''_j の条件式は、式(2)-(4)を代入して直ちに求まります。


[  y''_j の条件式]

  •  2\Delta x_1 y''_0+\Delta x_1 y''_1=6(s_0-\alpha)
  •  2\Delta x_N y''_N+\Delta x_N y''_{N-1}=6(\beta-s_{N-1})


[ 解くべき式 ]

 \displaystyle
\begin{pmatrix}
2\Delta x_1 & \Delta x_1 & 0 & \cdots & \cdots & 0 \\ \hline
\Delta x_1 & 2(\Delta x_1+\Delta x_2) & \Delta x_2 & & \huge{0} & \\
&\Delta x_2 & \ddots & \ddots & & \\
& & \ddots & \ddots & \Delta x_{N-1} & \\
& \huge{0} & & \Delta x_{N-1} & 2(\Delta x_{N-1}+\Delta x_N) & \Delta x_N \\ \hline
0 & \cdots & \cdots & 0 & \Delta x_N & 2\Delta x_N \\
\end{pmatrix}
\begin{pmatrix}
y''_0 \\
\vdots \\
y''_N \\
\end{pmatrix}
=
\begin{pmatrix}
6(s_0-\alpha) \\ \hline
6\Delta s_1 \\
\vdots \\
6\Delta s_{N-1} \\ \hline
6(\beta-s_{N-1}) \\
\end{pmatrix}


  ここでも係数行列が3重対角行列になっており、3重対角行列アルゴリズムが有効です。

ノットなしスプライン (not-a-knot spline)

  ノット(節点)とは、隣接する区間の曲線が互いに結合する点(データ点)のことです。ノットなしスプラインでは、 x=x_1,x_{N-1} で3階微分が連続になるように  y''_j を決めます。自然スプラインや固定スプラインと異なり、最端点  x=x_0, x_N での1階、2階微分の値を指定しません。

 \displaystyle
f'''_1 ( x = x_1 ) = 6a_1 = 6a_2 = f'''_2 ( x = x_1 ) \\
f'''_{N-1}(x=x_{N-1}) = 6a_{N-1} = 6a_N = f'''_N(x=x_{N-1})


最端点での1階、2階微分の値が不明な場合、ノットなしスプラインが有用です。

  ノットなしスプラインでは、 x=x_1,x_{N-1} で0~3階微分がすべて連続になるため、 f_1(x) f_2(x) f_{N-1}(x) f_N(x) はそれぞれ全く同じ関数になります。つまり、見かけ上  x=x_1,x_{N-1} のノットが消失します。 y''_j の条件式は、式(3)を代入して直ちに求まります。


[  y''_j の条件式]

  •  \Delta x_2 y''_0 - (\Delta x_1 + \Delta x_2) y''_1 + \Delta x_1 y''_2 = 0
  •  \Delta x_N y''_{N-2} - (\Delta x_{N-1} + \Delta x_N) y''_{N-1} + \Delta x_{N-1} y''_N = 0


[ 解くべき式 ]

 \displaystyle
\begin{pmatrix}
\Delta x_2 & -(\Delta x_1+\Delta x_2) & [\Delta x_1] & 0 & \cdots & 0 \\ \hline
\Delta x_1 & 2(\Delta x_1+\Delta x_2) & \Delta x_2 & & \huge{0} & \\
&\Delta x_2 & \ddots & \ddots & & \\
& & \ddots & \ddots & \Delta x_{N-1} & \\
& \huge{0} & & \Delta x_{N-1} & 2(\Delta x_{N-1}+\Delta x_N) & \Delta x_N \\ \hline
0 & \cdots & 0 & [\Delta x_N] & -(\Delta x_{N-1}+\Delta x_N) & \Delta x_{N-1} \\
\end{pmatrix}
\begin{pmatrix}
y''_0 \\
\vdots \\
y''_N \\
\end{pmatrix}
=
\begin{pmatrix}
0 \\ \hline
6\Delta s_1 \\
\vdots \\
6\Delta s_{N-1} \\ \hline
0 \\
\end{pmatrix}


[ ] で囲った要素により、係数行列が3重対角行列になっていません。第2行の  \Delta x_1 / \Delta x_2 倍を第1行から引き、第  N 行の  \Delta x_N / \Delta x_{N-1} 倍を第  (N+1) 行から引くことで、[ ] で囲った要素を消去します。適当に定数倍して式を整えると、


 \displaystyle
\begin{pmatrix}
\Delta x_1-\Delta x_2 & 2\Delta x_1+\Delta x_2 & 0 & \cdots & \cdots & 0 \\ \hline
\Delta x_1 & 2(\Delta x_1+\Delta x_2) & \Delta x_2 & & \huge{0} & \\
&\Delta x_2 & \ddots & \ddots & & \\
& & \ddots & \ddots & \Delta x_{N-1} & \\
& \huge{0} & & \Delta x_{N-1} & 2(\Delta x_{N-1}+\Delta x_N) & \Delta x_N \\ \hline
0 & \cdots & \cdots & 0 & 2\Delta x_N+\Delta x_{N-1} & \Delta x_N-\Delta x_{N-1} \\
\end{pmatrix}
\begin{pmatrix}
y''_0 \\
\vdots \\
y''_N \\
\end{pmatrix}
=
\begin{pmatrix}
6\frac{\Delta s_1}{1+\Delta x_2 / \Delta x_1} \\ \hline
6\Delta s_1 \\
\vdots \\
6\Delta s_{N-1} \\ \hline
6\frac{\Delta s_{N-1}}{1+\Delta x_{N-1} / \Delta x_N} \\
\end{pmatrix}


を得ます。これで、3重対角行列アルゴリズムを使える形になりました。

<注意>
 \Delta x_1-\Delta x_2 = 0 または  \Delta x_N-\Delta x_{N-1} = 0 の場合、3重対角行列アルゴリズムをそのまま適用するとゼロ除算が起きます。この場合は、 y''_1, y''_{N-1} についてそれぞれ独立に解けますので、これらを消去してから適用します。

 \displaystyle
\begin{pmatrix}
\hline
\Delta x_1 & \Delta x_2 & & & & \\
0 & 2(\Delta x_2+\Delta x_3) & \Delta x_3 & & \huge{0} & \\
&\Delta x_3 & \ddots & \ddots & & \\
& & \ddots & \ddots & \Delta x_{N-2} & \\
& \huge{0} & & \Delta x_{N-2} & 2(\Delta x_{N-2}+\Delta x_{N-1}) & 0 \\
& & & & \Delta x_{N-1} & \Delta x_N \\ \hline
\end{pmatrix}
\begin{pmatrix}
y''_0 \\
y''_2 \\
\vdots \\
y''_{N-2} \\
y''_N \\
\end{pmatrix}
=
\begin{pmatrix}
\hline
6\Delta s_1 -2(\Delta x_1+\Delta x_2)y''_1 \\
6\Delta s_2 -\Delta x_2y''_1 \\
6\Delta s_3 \\
\vdots \\
6\Delta s_{N-3} \\
6\Delta s_{N-2} -\Delta x_{N-1}y''_{N-1}\\
6\Delta s_{N-1} -2(\Delta x_{N-1}+\Delta x_N)y''_{N-1}\\ \hline
\end{pmatrix}


 \displaystyle
y''\_1 = 6\frac{\Delta s_1}{1+\Delta x_2 / \Delta x_1}\frac{1}{2\Delta x_1+\Delta x_2}
 \displaystyle
y''_{N-1} = 6\frac{\Delta s_{N-1}}{1+\Delta x_{N-1} / \Delta x_N}\frac{1}{2\Delta x_N+\Delta x_{N-1}}


  上記の場合以外で、 \Delta x_1-\Delta x_2 または  \Delta x_N-\Delta x_{N-1} 0 に近い場合は、計算が不安定になると思われます。この場合は、ガウスの掃き出し法など、別のアルゴリズムを使った方が良いかもしれません。

周期的スプライン (periodic spline)

  周期的スプラインでは、両最端点  x=x_0, x_N での1階、2階微分の値が互いに一致するように  y''_j を決めます。 y_0=y_N であれば、スプライン曲線は区間  [x_0,x_N] の外に周期的に繋がります。

 \displaystyle
f'_1(x=x_0) = 3a_1(-\Delta x_1)^2+2b_1(-\Delta x_1)+c_1 = c_N = f'_N(x=x_N) \\
f''_1(x=x_0) = 6a_1(-\Delta x_1)+2b_1 = 2b_N = f''_N(x=x_N)


 y''_j の条件式は、式(2)-(4)を代入して直ちに求まります。


[  y''_j の条件式]

  •  2\Delta x_1 y''_0+\Delta x_1 y''_1+\Delta x_N y''_{N-1}+2\Delta x_N y''_N=6(s_0-s_{N-1})
  •  y''_0-y''_N=0


[ 解くべき式 ]

 \displaystyle
\begin{pmatrix}
2\Delta x_1 & \Delta x_1 & 0 & \cdots & 0 & \Delta x_N & 2\Delta x_N \\ \hline
\Delta x_1 & 2(\Delta x_1+\Delta x_2) & \Delta x_2 & & & \huge{0} & \\
&\Delta x_2 & \ddots & & \ddots & & \\
& & \ddots & & \ddots & \Delta x_{N-1} & \\
& \huge{0} & & & \Delta x_{N-1} & 2(\Delta x_{N-1}+\Delta x_N) & \Delta x_N \\ \hline
1 & 0 & \cdots & \cdots & \cdots & 0 & -1 \\
\end{pmatrix}
\begin{pmatrix}
y''_0 \\
\vdots \\
y''_N \\
\end{pmatrix}
=
\begin{pmatrix}
6(s_0-s_{N-1}) \\ \hline
6\Delta s_1 \\
\vdots \\
6\Delta s_{N-1} \\ \hline
0 \\
\end{pmatrix}


3重対角行列アルゴリズムを適用するために変形を行います。第  (N+1) 行の  2\Delta x_N 倍を第  1 行、  \Delta x_N 倍を第  N 行に加えます。このとき、第  (N+1) 行の式  y''_0 - y''_N=0 を別に書くと、解くべき式は次の通りになります。

 \displaystyle
\begin{pmatrix}
2(\Delta x_N+\Delta x_1) & & \Delta x_1 & 0 & \cdots & 0 & & [\Delta x_N] \\ \hline
\Delta x_1 & & 2(\Delta x_1+\Delta x_2) & \Delta x_2 & & & \huge{0} & \\
& & \Delta x_2 & \ddots & & \ddots & & \\
& \huge{0} & & \ddots & & \ddots & & \Delta x_{N-1} \\
[\Delta x_N] & & & & & \Delta x_{N-1} & & 2(\Delta x_{N-1}+\Delta x_N) \\ \hline
\end{pmatrix}
\begin{pmatrix}
y''_0 \\
\vdots \\
y''_{N-1} \\
\end{pmatrix}
=
\begin{pmatrix}
6(s_0-s_{N-1}) \\ \hline
6\Delta s_1 \\
\vdots \\
6\Delta s_{N-1} \\ \hline
\end{pmatrix} \\


 \displaystyle
y''\_0 - y''\_N=0


右上と左下の角に非ゼロの要素([ ] で囲った要素)がありますが、これも3重対角行列アルゴリズムを適用できる形です。例えば、次のWikipediaのページ ("Variants") を参照。

en.wikipedia.org


各バリエーションの比較テスト

自然スプライン、固定スプライン、ノットなしスプライン

FORTRANで即席のプログラムを作成しました。SPLINE_INT() を呼び出して係数を決定したのち、SPLINE() を呼び出して区間  [x_0, x_N] の任意の点を計算します。また、SPLINE_INT() では、左端と右端とで別々に3次スプラインの種類を指定します。


"input1.txt" からデータ点を読み取ってスプライン補間し、結果を "spline.txt" に出力します。ここでは次の適当なデータ点で試してみます。


"input1.txt"

0.2   0.4392
0.7   0.8638
1.6   0.5449
2.3   0.2019
3.0   0.0190
4.0  -0.0374


  補間の結果を、下の図2に示します。

図2 3次スプライン補間の計算結果

固定スプラインの結果は、最端点  x=0.2, 4.0 でやや無理な傾きを指定しているため  (\alpha=-1, \beta=1)、自然スプラインとノットなしスプラインの結果に比べて大きくたわんでいます。一方、自然スプラインとノットなしスプラインの結果の差は、図の左側で大きくなっているように見えます。最左端  x=0.2 の付近を拡大してみます。

図3 左端点付近の拡大図。接線との比較

  図3では、 x=0.2 での接線を点線で描き加えました(係数が分かっているので、当然接線を求めることができます)。 x が増加したときの接線との乖離の程度を見比べると、明らかに自然スプラインの方が直線に近い形をしていることが分かります。

図4 最左端付近の拡大図。 f_1(x) との比較

  図4では、 f_1(x) を点線で描き加えました。自然スプラインは  x\gt x_1 で乖離していますが、一方でノットなしスプラインは  x\gt x_2 で乖離しています。見かけ上  x=x_1 のノットが消失していることが分かります。

周期的スプライン

FORTRANで即席のプログラムを作成しました。SPLINE_INT() を呼び出して係数を決定したのち、SPLINE() を呼び出して区間 [x0,xN] の任意の点を計算します。


"input2.txt" からデータ点を読み取ってスプライン補間し、結果を "splineP.txt" に出力します。ここでは次の適当なデータ点  (y=\sin x) で試してみます。


"input2.txt"

0.7854   0.7071
1.0472   0.8660
1.5708   1.0000
2.0944   0.8660
2.3562   0.7071
2.6180   0.5000
3.1416  -0.0004
3.6652  -0.5000
3.9270  -0.7071
4.1888  -0.8660
4.7124  -1.0000
5.2360  -0.8660
5.4978  -0.7071
5.7596  -0.5000
6.2832   0.0000
6.8068   0.5000
7.0686   0.7071


  補間の結果を、下の図5に示します。

図5 周期的スプラインによる周期関数の補間

補間を行った区間は、中央の縦線 (x=2.25\pi) より左側です。右側の曲線は、左側の補間の結果を1周期ずらしてプロットしたものです。1階微分を破線、2階微分を点線であわせて示しました。いずれも、縦線のところで滑らかにつながっています。

  比較のため、自然スプライン、固定スプライン、ノットなしスプラインによる補間の結果を示します。

図6 自然スプラインによる周期関数の補間

  図6は自然スプラインの結果です。実線の結果は悪くないように見えますが、1階微分は明らかに不連続です。2階微分は、最端点でゼロになる条件に無理があり、縦線の付近で不自然な振る舞いをしています。

図7 固定スプラインによる周期関数の補間

  図7は固定スプラインの結果です。両最端点での1階微分は、どちらも  \sin'(2.25\pi) =1/\sqrt{2} としました。1階、2階微分とも滑らかにつながっているように見えます(実際に滑らかにつながっている訳ではありません)。最端点での1階微分の値が予め分かっているため、それらしい結果が得られました。

図8 ノットなしスプラインによる周期関数の補間

  図8はノットなしスプラインの結果です。1階微分まで滑らかにつながっているように見えます(実際に滑らかにつながっている訳ではありません)。2階微分は明らかに不連続ですが、自然スプラインの結果と比べると、こちらの結果の方が無理がありません。


まとめ

  3次スプラインの4つのバリエーションをまとめました。また、即席のプログラムで実際にスプライン補間を行い、それぞれの特徴を見ました(表1)。

表1 3次スプラインのバリエーション
周期的スプライン 両最端点の1階、2階微分の値を互いに一致させる。
周期的なデータの補間に適する。
自然スプライン最端点での2階微分の値をゼロとする。
直線で無理なく補外できるデータの補間に適する。
固定スプライン最端点での1階微分の値を指定する。
最端点での傾きが分かっているデータの補間に適する。
ノットなしスプライン 最端点での条件を指定しない。
一般的なデータの補間に用いられる。