Cubic Spline Interpolation
by Ryan Anderson and Heidi Scarff
The most common piecewise polynomial approximation uses cubic polynomials between each successive
pair of nodes and is called cubic spline interpolation. The following will show how to use two kinds of
cubic spline interpolations, natural cubic spline and clamped cubic spline. These methods are easier than
Hermite interpolation because they do not require us to know the derivatives, except at the endpoints of the
intervals being approximated. There is sufficient flexibility in this procedure to ensure that the interpolant
has a continuous first and second derivative on the interval.
Definition of a Cubic Spline Interpolation (3.10)
Given a function f defined on [a,b] and a set of nodes a = x0 < x1 << xn = b, a cubic spline interpolant
S for f is a function that satisfies the following conditions:
a. S(x) is a cubic polynomial. It is denoted Sj (x) on the interval [xj , xj+1] for each j = 0,1,n-1; i.e. S0(x) is on the subinterval [x0 , x1]
a. S(xj) = f(xj) for each j = 0,1,2,,n; Note: we cannot assume that the derivatives of the interpolant agree with those of the function, even at the nodes. f'(x)S'(x)
b. Sj+1(xj+1) = Sj(xj+1) for each j = 0,1,2,,n-2;
c. S'j+1(xj+1) = S'j(xj+1) for each j = 0,1,2,,n-2;
d. S''j+1(xj+1) = S''j(xj+1) for each j = 0,1,2,,n-2;
e. One of the following boundary conditions is true:
i) S''(x0) = S''(xn) = 0 free or natural boundary
ii) S'(x0) = f'(x0) and S'(xn) = f'(xn) clamped boundary
When the free boundary conditions occur, the spline is called a natural spline. The clamped boundary
conditions lead to more accurate approximations because they include more information about the
function. However, it is necessary to have either the values of the derivatives at each endpoint or a close
approximation.
Solving for coefficients
To construct the cubic spline interpolant for a given function f, we need to solve for the coefficients a,b,c,
and d. To do this, the conditions in the definition are applied to the cubic polynomials of the form
Sj(x) = aj+ bj(x - xj) + cj(x - xj)2 + dj(x - xj)3
for each j = 0,1,,n-1.
From here we can easily see that
Sj(xj) = aj = f(xj).
Now if we apply condition ( c ) of the definition above
(1) Sj+1(xj+1) = aj+1 which implies
(2) aj+1 = Sj+1(xj+1) = Sj(xj+1) = aj + bj(xj+1 - xj)
+ cj(xj+1 - xj)2 + dj(xj+1 - xj)3
for each j = 0,1,,n-2.
To make things a little easier, let
(3) hj = xj+1 - xj
for each j = 0,1,,n-1.
Now, define an = f(xn). Then
(4) aj+1 = aj + bjhj + cjh2j + djh3j
holds for each j = 0,1,,n-1.
Similarly, we can define bn = S'(xn).
(5) S'j (x) = bj +2cj (x - xj )+ 3dj (x - xj )2
Which implies that S'j(xj) = bj for each j = 0,1,,n-1.
Applying condition (d) gives
(6) bj+1 = bj + 2cj hj + 3dj h2 j
for each j = 0,1,,n-1.
Now define cn = (S''(xn)/2).
Applying condition (e) gives
(7) cj+1 = cj + 3dj hj
for each j = 0,1,,n-1.
Solving for dj in (7) and the substituting this value into (5) and (6) gives the following equations.
(1) aj+1 = aj + bj hj +(1/3)h2 j( 2j + cj+1)
and
(1) bj+1 = bj + hj (cj+cj+1)
for each j = 0,1,,n-1.
Next, solve equation (8) first for bj.
(1) bj=(1/ hj)(aj+1 - aj) -(1/3) hj(2cj + cj+1)
Now, reduce the index of (10) to bj-1 and the equation becomes:
(1) bj-1 = (1/hj-1)(aj - aj-1) -(1/3) hj-1(2cj-1 + cj)
Substitute these values into (9), with a reduced index of one. This produces the following linear systems of
equations:
(1) hj-1cj-1 +2(hj-1-hj)cj + hjcj+1 = (1/3)hj(aj+1-aj)-(3/hj-1)(aj-aj-1),
for each j = 1,2,,n-1.
Theorem 3.11 (p.147)
If f is defined at a = x0< x1<< xn = b, then f has a unique natural spline interpolation on the nodes x0,
x1, , xn. In other words, it has a spline the satisfies the boundary conditions S''(a) = 0 and S''(b)=0.
Natural Cubic Spline (Algorithm 3.4 p.148)
To construct the cubic spline interpolant S for the function f, defined at the numbers x0< x1<< xn,
satisfying S''(x0) = S''(xn) = 0.
INPUT n; x0 , x1 , , xn;a0 = f(x0), a1= f(x1), , an = f(xn).
OUTPUT ai , bi , ci , di for i = 0,1,,n-1. (Note: S(x) = Si(x) = ai+ bi(x - xi) + ci(x - xi)2 +
di(x - xi)3 for xi<= x<= xi+1.)
Step 1 For i = 0,1,,n-1 set
hi = xi+1 - xi.
Step 2 For i = 1,2,,n-1 set
yi = (3/hi )(ai+1 - ai) - (3/hi-1)(ai -ai-1).
(Steps 3-5 and part of Step 6 solve a tridiagonal linear system using the method described in Algorithm 6.7)
Step 3 Set l0 = 1;
u0 = 0;
z0 = 0;
Step 4 For i = 1,2,,n-1 set
li = 2(xi+1 - xi-1) - hi-1ui-1;
ui = hi/li
zi = (yi - hi-1 zi-1)/li.
Step 5 Set ln = 1;
zn = 0;
cn = 0;
Step 6 For i = n-1,n-2,,0 set
ci = zi - ui ci+1;
bi = (ai+1 - ai)/hi - hi(ci+1 +2 ci)/3;
di = (ci+1 - ci)/(3hi);
Step 7 OUTPUT (ai , bi , ci , di for i = 0,1,,n-1)
STOP.
Theorem 3.12 (p148)
If f is defined at a = x0<x1<... < xn = b and differentiable at a and b, then f has a unique clamped spline
interpolant on the nodes x0, x1,...,xn, that is a spline interpolant athat satisfies the boundary conditions
S'(a)=f'(a) and S'(b)=f'(b).
Clamped Cubic Spline Algorithm (3.5)
Input:
n,
x0...xn
ai=f(xi) , 0<=i<=n
FPO = f'(xo)
FPN = f'(xn)
Output:
aj,bj,cj,dj for j=0,1...n-1
Step 1 for i=0,1,...,n-1 set hi=xi+1 - xi
Step 2 Set 0 = 3(a1-a0)/h0 - 3*FPO
n=3*FPN-3(an-an-1)/hn-1
Step 3 For i=1,2,...,n-1
set I = 3/hi (ai+1-ai)-3/hi-1(ai-ai-1)
Step 4 Set l0 = 2h0
µ0 = 0.5
z0 = 0/l0
Step 5 For i=1,2,...,n-1
set li = 2(xi+1 - xi-1) - hi-1µi-1
µi - hi/li
zi = (i-hi-1zi-1)/li
Step 6 Set ln = hn-1(2-µn-1)
zn = (n-hn-1zn-1)/ln
cn=zn
Step 7 For j=n-1,n-2,...,0
set cj=zj-µjcj+1
bj=(aj+1-aj)/hj - hj(cj+1+2cj)/3
dj=(cj+1-cj)/(3hj)
Step 8 Output (aj,bj,cj,dj, for j=0,1,...,n-1)
Stop.
Algorithm 3.4 - Output
This is the output from algorithm 3.4, solving problem 11 from page 155:
The total number of intervals = 4
Enter the numbers x
0.0
0.25
0.5
0.75
1.00
Now the values of f(x):
1.0
0.707107
0.0
-0.707107
-1.0
i x[i] a[i] b[i] c[i] d[i]
0 0 1 -0.757358 0 -6.62742
1 0.25 0.707107 -2 -4.97057 6.62742
2 0.5 0 -3.24264 -1.0842e-19 6.62742
3 0.75 -0.707107 -2 4.97057 -6.62742
This agrees substantially with the answer on page 746
Implementation:
#include <iostream.h>
#include <math.h>
void main()
{ int i,n;
long double *x,*h,*y,*a,*b,*c,*d,*l,*u,*z;
cout<<"The total number of intervals = ";
cin>>n;
x=new long double[n+1];
h=new long double[n+1];
y=new long double[n+1];
a=new long double[n+1];
b=new long double[n+1];
c=new long double[n+1];
d=new long double[n+1];
l=new long double[n+1];
u=new long double[n+1];
z=new long double[n+1];
cout<<"Enter the numbers x"<<endl;
for (i=0;i<n+1;i++){
cin>>x[i];
}
cout << "Now the values of f(x):" ;
for (i=0;i<n+1;i++){
cin>>a[i];
}
for (i=0;i<n;i++){
h[i]=x[i+1] - x[i];
}
for (i=1;i<n;i++){
y[i]=(3/h[i])*(a[i+1]-a[i])-(3/h[i-1])*(a[i]-a[i-1]);
}
l[0]=1.0;
u[0]=0.0;
z[0]=0.0;
for (i=1;i<n;i++){
l[i]=2*(x[i+1]-x[i-1])-h[i-1]*u[i-1];
u[i]=h[i]/l[i];
z[i]=(y[i]-h[i-1]*z[i-1])/l[i];
}
l[n]=1.0;
z[n]=0.0;
c[n]=0.0;
for (i=n-1;i>=0;i--){
c[i]=z[i]-u[i]*c[i+1];
b[i]=(a[i+1]-a[i])/h[i] - h[i]*(c[i+1]+2*c[i])/3;
d[i]=(c[i+1]-c[i])/(3*h[i]);
}
cout<<"i "<<"x[i] "<<"a[i] "<<"b[i] "<<"c[i] "<<"d[i]"<<endl;
for(i=0;i<n;i++)
cout<<i<<" " << x[i] << " "<< a[i] <<" " << b[i] <<" " << c[i] <<" "<< d[i] << endl;
}
Algorithm 3.5, solving problem 13 on page 155
(the same as problem 11, except with clamped spline interpolation)
How many intervals?
4
Input the x coords:
0.0
0.25
0.5
0.75
1.00
thanks, now the f(x)'s
1.0
0.707107
0.0
-0.707107
-1.0
Enter f'(x0) and f'(xn) please
0.0
0.0
The spline follows
1 0 -5.19332 2.02812
0.707107 -2.21639 -3.67223 4.89631
6.12303e-17 -3.13445 -1.25442e-16 4.89631
-0.707107 -2.21639 3.67223 2.02812
Note, these columns refer to ai, bi, ci, and di, respectively.
Implementation:
#include <math.h>
#include <iostream.h>
void ccspline(int n, long double *x, long double *a, long double FPO, long double FPN)
{
long double *b, *c, *d, *h, *e, *z, *u, *l;
b= new long double[n+1];
c= new long double[n+1];
d= new long double[n+1];
h= new long double[n+1];
e= new long double[n+1];
z= new long double[n+1];
u= new long double[n+1];
l= new long double[n+1];
int i;
for (i=0;i<n;i++)
h[i]=x[i+1]-x[i];
e[0]=3*(a[1]-a[0])/h[0]-3*FPO;
e[n]=3*FPN-3*(a[n]-a[n-1])/h[n-1];
for (i=1;i<n;i++)
e[i]=3/h[i]*(a[i+1]-a[i])-3/h[i-1]*(a[i]-a[i-1]);
l[0]=2*h[0];
u[0]=0.5;
z[0]=e[0]/l[0];
for(i=1;i<n;i++)
{
l[i]=2*(x[i+1]-x[i-1]) - h[i-1]*u[i-1];
u[i] = h[i]/l[i];
z[i] = ( e[i]-h[i-1]*z[i-1])/l[i];
};
l[n]=h[n-1]*(2-u[n-1]);
z[n]=(e[n]-h[n-1]*z[n-1])/l[n];
c[n]=z[n];
for(i=n-1;i>=0;i--)
{
c[i]=z[i]-u[i]*c[i+1];
b[i]=(a[i+1]-a[i])/h[i]-h[i]*(c[i+1]+2*c[i])/3;
d[i]=(c[i+1]-c[i])/(3*h[i]);
};
for(i=0; i<n;i++)
cout << a[i] << ", " << b[i]<<", "<<c[i]<<", "<<d[i]<<endl;
};
void main(void)
{
int n,i;
long double *a,*x,FPO,FPN;
cout << " How many intervals?" ;
cin >> n;
a=new long double[n+1];
x=new long double[n+1];
cout << endl << " Input the x coords: " << endl;
for (i=0;i<n+1;i++)
cin >> x[i];
cout << endl << " thanks, now the f(x)'s " << endl;
for (i=0;i<n+1;i++)
cin >> a[i];
cout << endl<< " Enter f'(x0) and f'(xn) please " << endl;
cin >> FPO;
cin >> FPN;
cout << " The spline follows " << endl;
ccspline(n,x,a,FPO,FPN);
};
This graph shows cubic spline interpolation of cos(*x) on the interval [0,1] as well as the actual graph. They have been separated by 5 pixels to simplify viewing. The bottom graph is the actual function, the upper graph is the interpolated function. Note the similarity between thm.