Lagrange插值法
考慮有n n n 個不同的點 ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x i , y i ) … ( x n , y n ) {(x_1,y_1),(x_2,y_2),(x_i,y_i)\dots (x_n,y_n)} ( x 1 , y 1 ) , ( x 2 , y 2 ) , ( x i , y i ) … ( x n , y n ) ,定義函數f i ( x ) f_i(x) f i ( x ) 滿足在l i ( x j ) l_i(x_j) l i ( x j ) 滿足克羅內克符號函數 δ i j \delta_{ij} δ i j ,
l i ( x j ) = δ i j = { 1 i = j 0 i ≠ j l_i(x_j)=\delta_{ij}=\begin{cases}1\quad\quad\quad i=j\\0\quad\quad\quad i\not=j\end{cases} l i ( x j ) = δ i j = { 1 i = j 0 i = j
此時,若另L ( x ) = ∑ i = 1 n y i l i ( x ) L(x)=\sum_{i=1}^n y_il_i(x) L ( x ) = i = 1 ∑ n y i l i ( x )
則對於任意x i x_i x i 有
y i = l i ( x i ) y_i=l_i(x_i) y i = l i ( x i )
即n n n 個點必然經過L ( x ) L(x) L ( x ) ,即爲所求。
再考慮l i ( x ) l_i(x) l i ( x ) ,其應爲一個n − 1 n-1 n − 1 次多項式,則可由因式法寫出滿足要求的函數:
l i ( x ) = ( x − x 1 ) ( x − x 2 ) ( x − x 3 ) … ( x − x i − 1 ) ( x − x i + 1 ) … ( x i − x n ) ( x i − x 1 ) ( x i − x 2 ) ( x i − x 3 ) … ( x i − x i − 1 ) ( x i − x i + 1 ) … ( x i − x n ) = ∏ j ≠ i 1 ≤ j ≤ n x − x j x i − x j \begin{aligned}
l_i(x)&=\frac{(x-x_1)(x-x_2)(x-x_3)\dots(x-x_{i-1})(x-x_{i+1})\dots(x_i-x_n)}{(x_i-x_1)(x_i-x_2)(x_i-x_3)\dots(x_i-x_{i-1})(x_i-x_{i+1})\dots(x_i-x_n)}\\
\\
&=\prod_{j\not=i}^{
1\le j \le n }\frac{x-x_j}{x_i-x_j}
\end{aligned}
l i ( x ) = ( x i − x 1 ) ( x i − x 2 ) ( x i − x 3 ) … ( x i − x i − 1 ) ( x i − x i + 1 ) … ( x i − x n ) ( x − x 1 ) ( x − x 2 ) ( x − x 3 ) … ( x − x i − 1 ) ( x − x i + 1 ) … ( x i − x n ) = j = i ∏ 1 ≤ j ≤ n x i − x j x − x j
這裏的l i ( x ) l_i(x) l i ( x ) 稱爲插值基函數。
以下是代碼實現
#include <stdio.h>
#include <stdlib.h>
float x[ 7 ] = { 1.20 , 1.24 , 1.28 , 1.32 , 1.36 , 1.40 } ;
float y1[ 7 ] = { 1.09545 , 1.11355 , 1.13137 , 1.14891 , 1.16619 , 1.18322 } ;
float y2[ 7 ] = { 0.07918 , 0.09342 , 0.10721 , 0.12057 , 0.13354 , 0.14613 } ;
float xi[ 6 ] = { 1.22 , 1.26 , 1.30 , 1.34 , 1.38 } ;
float Lagrange ( float * y, float cx)
{
int n= 6 ;
float temp[ 10 ] , ans= 0 ;
for ( int i= 0 ; i< n; i++ )
{
temp[ i] = y[ i] ;
for ( int j= 0 ; j< n; j++ )
if ( j!= i)
temp[ i] * = ( cx - x[ j] ) / ( x[ i] - x[ j] ) ;
ans + = temp[ i] ;
}
return ans;
}
int main ( )
{
for ( int i = 0 ; i < 5 ; i++ )
printf ( "當x=%.2f,y1=%.5f,y2=%.5f\n" , xi[ i] , Lagrange ( y1, xi[ i] ) , Lagrange ( y2, xi[ i] ) ) ;
system ( "pause" ) ;
return 0 ;
}
我們可以發現假如新加入一個點,就必須得重新計算,這是它的缺點.
Newton插值法
該方法確定了一組新的基函數,確保能加入新的點能夠重用之前的計算結果:
ϕ 1 ( x ) = 1 ϕ 2 ( x ) = ( x − x 1 ) ϕ 3 ( x ) = ( x − x 1 ) ( x − x 2 ) ⋯ = ⋯ ϕ n + 1 ( x ) = ∏ i = 1 n ( x − x i )
\begin{aligned}
\phi_{1}(x) &=1 \\
\phi_{2}(x) &=\left(x-x_{1}\right) \\
\phi_{3}(x) &=\left(x-x_{1}\right)\left(x-x_{2}\right) \\
\cdots &=\cdots \\
\phi_{n+1}(x) &=\prod_{i=1}^{n}\left(x-x_{i}\right)
\end{aligned}
ϕ 1 ( x ) ϕ 2 ( x ) ϕ 3 ( x ) ⋯ ϕ n + 1 ( x ) = 1 = ( x − x 1 ) = ( x − x 1 ) ( x − x 2 ) = ⋯ = i = 1 ∏ n ( x − x i )
可以看到由於ϕ n + 1 ( x ) = ϕ n ( x ) ( x − x n ) \phi_{n+1}(x)=\phi_{n}(x)(x-x_{n}) ϕ n + 1 ( x ) = ϕ n ( x ) ( x − x n ) ,因此可以重用之前的結果。
則最終的多項式爲:
N ( x ) = ∑ i = 1 n + 1 a i ϕ i ( x )
N(x)=\sum_{i=1}^{n+1}a_i\phi_i(x)
N ( x ) = i = 1 ∑ n + 1 a i ϕ i ( x )
現在僅僅需要確定a i a_i a i 的值就可以確定N ( x ) N(x) N ( x ) 。
我們將每個點依次帶入相減可得到一個神奇的規律:
a 1 = y 1 a 2 = y 2 − y 1 x 2 − x 1 a 3 = y 3 − y 2 x 3 − x 2 − y 2 − y 1 x 2 − x 1 x 3 − x 1 ⋮
\begin{aligned}
a_1&=y_1\\
a_2&=\frac{y_2-y_1}{x_2-x_1}\\
a_3&=\frac{\frac{y_3-y_2}{x_3-x_2}-\frac{y_2-y_1}{x_2-x_1}}{x_3-x_1}\\
\vdots
\end{aligned}
a 1 a 2 a 3 ⋮ = y 1 = x 2 − x 1 y 2 − y 1 = x 3 − x 1 x 3 − x 2 y 3 − y 2 − x 2 − x 1 y 2 − y 1
我們把這種叫做差商 ,0階均差定義爲f [ x i ] = f ( x i ) f[x_i]=f(x_i) f [ x i ] = f ( x i ) ,n − 1 n-1 n − 1 階差商爲:
f [ x 1 , x 2 , x 3 … x n ] = f [ x 1 , x 2 , x 3 … x n − 1 ] − f [ x 2 , x 3 , x 4 … x n ] x n − x 1
f[x_1,x_2,x_3\dots x_n]=\frac{f[x_1,x_2,x_3\dots x_{n-1}]-f[x_2,x_3,x_4\dots x_n]}{x_n-x_1}
f [ x 1 , x 2 , x 3 … x n ] = x n − x 1 f [ x 1 , x 2 , x 3 … x n − 1 ] − f [ x 2 , x 3 , x 4 … x n ]
下面是差商表,每次迭代也可重用上一步的結果
所以最終爲
N ( x ) = f ( x 1 ) + f [ x 1 , x 2 ] ( x − x 1 ) + f [ x 1 , x 2 , x 3 ] ( x − x 1 ) ( x − x 2 ) + f [ x 1 , … , x 4 ] ( x − x 1 ) ( x − x 2 ) ( x − x 3 ) + …
\begin{aligned}
N(x)=& f\left(x_{1}\right)+\\
& f\left[x_{1}, x_{2}\right]\left(x-x_{1}\right)+\\
& f\left[x_{1}, x_{2}, x_{3}\right]\left(x-x_{1}\right)\left(x-x_{2}\right)+\\
& f\left[x_{1}, \ldots, x_{4}\right]\left(x-x_{1}\right)\left(x-x_{2}\right)\left(x-x_{3}\right)+\\
& \ldots
\end{aligned}
N ( x ) = f ( x 1 ) + f [ x 1 , x 2 ] ( x − x 1 ) + f [ x 1 , x 2 , x 3 ] ( x − x 1 ) ( x − x 2 ) + f [ x 1 , … , x 4 ] ( x − x 1 ) ( x − x 2 ) ( x − x 3 ) + …
以下是代碼,可想而知,雖然Newton插值效率提高了,但是也要多出一部分來計算差商
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
float x[ 7 ] = { 1.20 , 1.24 , 1.28 , 1.32 , 1.36 , 1.40 } ;
float y1[ 7 ] = { 1.09545 , 1.11355 , 1.13137 , 1.14891 , 1.16619 , 1.18322 } ;
float y2[ 7 ] = { 0.07918 , 0.09342 , 0.10721 , 0.12057 , 0.13354 , 0.14613 } ;
float xi[ 6 ] = { 1.22 , 1.26 , 1.30 , 1.34 , 1.38 } ;
float diff[ 10 ] [ 10 ] ;
float phi[ 10 ] ;
int n= 6 ;
void DifferenceQuotient ( float * y)
{
for ( int i = 0 ; i < n; i++ )
diff[ i] [ 0 ] = y[ i] ;
for ( int i = 1 ; i < n; i++ )
{
for ( int j = 1 ; j < i+ 1 ; j++ )
diff[ i] [ j] = ( diff[ i] [ j - 1 ] - diff[ i- 1 ] [ j - 1 ] ) / ( x[ i] - x[ i- j] ) ;
}
}
float Newton ( float * y, float cx)
{
phi[ 0 ] = 1 ;
float ans = 0 ;
for ( int i = 1 ; i < n; i++ )
phi[ i] = phi[ i- 1 ] * ( cx - x[ i- 1 ] ) ;
for ( int i = 0 ; i < n; i++ )
ans + = phi[ i] * diff[ i] [ i] ;
return ans;
}
int main ( ) {
DifferenceQuotient ( y1) ;
for ( int i = 0 ; i < 5 ; i++ )
printf ( "當x=%.2f,y1=%.5f\n" , xi[ i] , Newton ( y1, xi[ i] ) ) ;
cout << endl;
DifferenceQuotient ( y2) ;
for ( int i = 0 ; i < 5 ; i++ )
printf ( "當x=%.2f,y2=%.5f\n" , xi[ i] , Newton ( y2, xi[ i] ) ) ;
system ( "pause" ) ;
}
如果要新增節點,可以增量更新差商表
兩者關係
其實我們可以發現兩個方法都是通過n n n 個點確定了一組n n n 個方程的方程組:
{ y 1 = a 0 + a 1 x 1 + a 2 x 1 2 + ⋯ + a n x 1 n y 2 = a 0 + a 1 x 2 + a 2 x 2 2 + ⋯ + a n x 2 n ⋮ y n = a 0 + a 1 x n + a 2 x n 2 + ⋯ + a n x n n
\begin{cases}
y_1&=a_0+a_1x_1+a_2x_1^2+\dots+a_nx_1^n\\
y_2&=a_0+a_1x_2+a_2x_2^2+\dots+a_nx_2^n\\
\vdots\\
y_n&=a_0+a_1x_n+a_2x_n^2+\dots+a_nx_n^n\\
\end{cases}
⎩ ⎪ ⎪ ⎪ ⎪ ⎨ ⎪ ⎪ ⎪ ⎪ ⎧ y 1 y 2 ⋮ y n = a 0 + a 1 x 1 + a 2 x 1 2 + ⋯ + a n x 1 n = a 0 + a 1 x 2 + a 2 x 2 2 + ⋯ + a n x 2 n = a 0 + a 1 x n + a 2 x n 2 + ⋯ + a n x n n
矩陣形式爲Y = X A Y=XA Y = X A
[ y 1 y 2 ⋮ y n ] = [ 1 x 1 x 1 2 … x 1 n 1 x 2 x 2 2 … x 2 n ⋮ ⋮ ⋮ … ⋮ 1 x n x n 2 … x n n ] [ a 0 a 1 ⋮ a n ]
\begin{bmatrix} y_1\\y_2\\ \vdots \\y_n\end{bmatrix}=
\begin{bmatrix} 1&x_1&x_1^2&\dots&x_1^n\\1&x_2&x_2^2&\dots&x_2^n\\ \vdots&\vdots&\vdots&\dots&\vdots\\1&x_n&x_n^2&\dots&x_n^n\end{bmatrix}
\begin{bmatrix} a_0\\a_1\\ \vdots \\a_n\end{bmatrix}
⎣ ⎢ ⎢ ⎢ ⎡ y 1 y 2 ⋮ y n ⎦ ⎥ ⎥ ⎥ ⎤ = ⎣ ⎢ ⎢ ⎢ ⎡ 1 1 ⋮ 1 x 1 x 2 ⋮ x n x 1 2 x 2 2 ⋮ x n 2 … … … … x 1 n x 2 n ⋮ x n n ⎦ ⎥ ⎥ ⎥ ⎤ ⎣ ⎢ ⎢ ⎢ ⎡ a 0 a 1 ⋮ a n ⎦ ⎥ ⎥ ⎥ ⎤
係數矩陣X X X 爲範德蒙行列式,則∣ X ∣ ≠ 0 |X|\not =0 ∣ X ∣ = 0 ,因此可以得出其解A A A 唯一,故最終確定的多項式唯一,即兩者等效.Largrane法較爲簡單,而Newton法在需要新增節點時可以保持很好的效率。