For the spline interpolation one interpolation function is calculated for each interval between two supporting points.
If we have for instance a set of 6 supporting points, the interpolation would look like this:
For each of these intervals one cubic polynomial function is calculated like:
To make sure that the wave shapes of all this intervals fit together and build a smooth wave shape, there are some conditions to be fulfilled:
With N as the number of supporting points.
From these conditions and the substitution hi = xi+1 - xi the parameters a, b, c and d can be derived:
From condition a we get ai = yi. If x = xi, then x – xi = 0 and pi(x) = ai = yi
With the second deductions
and the substitution
inserted in the formulation of b
This resolved for di-1
And with this we get from b
And both put together
Resolved for bi-1:
Or bi:
This will be the equation to compute bi
And we’ll use further down
From c we get
Now, there is a boundary condition for natural spines: The interpolated graph shall continue in a straight line on both ends. That means
and
That means c1 = 0 for natural splines.
With this the equation leads to a matrix equation of the order N-2 for ci with i = 2 ..n-1 and the matrix.
Now there is only di left and this we get from condition d
The algorithm to calculate the parameters looks like:
public
bool
CalcParameters()
{
int i, k;
for (i = 0; i < order; i++) // calculate a parameters
{
a[i] = y_k[i];
}
for (i = 0; i < order - 1; i++)
{
h[i] = x_k[i + 1] - x_k[i];
}
// empty matrix for c parameters
for (i = 0; i < order - 2; i++)
{
for (k = 0; k < order - 2; k++)
{
m[i, k] = 0.0;
y[i] = 0.0;
x[i] = 0.0;
}
}
// fill matrix for c parameters
for (i = 0; i < order - 2; i++)
{
if (i == 0)
{
m[i, 0] = 2.0 * (h[0] + h[1]);
m[i, 1] = h[1];
}
else
{
m[i, i - 1] = h[i];
m[i, i] = 2.0 * (h[i] + h[i + 1]);
if (i < order - 3)
m[i, i + 1] = h[i + 1];
}
if ((h[i] != 0.0) && (h[i + 1] != 0.0))
y[i] = 0.0;
}
if (gauss.Eliminate())
{
gauss.Solve();
c[0] = 0.0; // boundry condition for natural splines
for (i = 1; i < order - 1; i++)
c[i] = x[i - 1];
for (i = 0; i < order - 1; i++) // calculate b and d parameters
{
if (h[i] != 0.0)
{
}
return true;
}
else
return false;
}
{
int i, k;
for (i = 0; i < order; i++) // calculate a parameters
{
a[i] = y_k[i];
}
for (i = 0; i < order - 1; i++)
{
h[i] = x_k[i + 1] - x_k[i];
}
// empty matrix for c parameters
for (i = 0; i < order - 2; i++)
{
for (k = 0; k < order - 2; k++)
{
m[i, k] = 0.0;
y[i] = 0.0;
x[i] = 0.0;
}
}
// fill matrix for c parameters
for (i = 0; i < order - 2; i++)
{
if (i == 0)
{
m[i, 0] = 2.0 * (h[0] + h[1]);
m[i, 1] = h[1];
}
else
{
m[i, i - 1] = h[i];
m[i, i] = 2.0 * (h[i] + h[i + 1]);
if (i < order - 3)
m[i, i + 1] = h[i + 1];
}
if ((h[i] != 0.0) && (h[i + 1] != 0.0))
y[i] = ((a[i + 2] - a[i + 1]) /
h[i + 1] - (a[i + 1] - a[i]) / h[i]) * 3.0;
elsey[i] = 0.0;
}
if (gauss.Eliminate())
{
gauss.Solve();
c[0] = 0.0; // boundry condition for natural splines
for (i = 1; i < order - 1; i++)
c[i] = x[i - 1];
for (i = 0; i < order - 1; i++) // calculate b and d parameters
{
if (h[i] != 0.0)
{
d[i] = 1.0 / 3.0 / h[i] *
(c[i + 1] - c[i]);
b[i] = 1.0 / h[i] * (a[i + 1] - a[i]) - h[i] / 3.0 * (c[i + 1] + 2 * c[i]);
}b[i] = 1.0 / h[i] * (a[i + 1] - a[i]) - h[i] / 3.0 * (c[i + 1] + 2 * c[i]);
}
return true;
}
else
return false;
}
The part for the interpolation is
public void Interpolate(int order, int resolution)
{
int i, k;
double timestamp;
for (i = 0; i < order - 1; i++)
{
for (k = 0; k < resolution; k++)
{
timestamp
= (double)(k) / (double)(resolution)
* h[i];
yp[i * resolution + k] = a[i] +
b[i] * (timestamp) + c[i] * Math.Pow(timestamp,
2) + d[i] * Math.Pow(timestamp,
3);
xp[i * resolution + k] = timestamp + x_k[i];
}xp[i * resolution + k] = timestamp + x_k[i];
}
}
This is the basic algorithm for Natural Splines.
But, that’s not all. The Matrix equation to calculate the h parameters contains many elements that are 0 and due to this fact there can by an improvement to solve this equation
To calculate the h parameters we have to solve a matrix equation of following structure:
Comparison of the parameters gives:
c3 = l2 * m2 c4 = l3 * m3 |
a2 = l1 * u1 + m1 a3 = l2 * u2 + m2 a4 = l3 * u3 + m3 |
b2 = u2 b3 = u3 b4 = u4 |
And from this for the elements:
li = ci+1 / mi
mi+1 = ai+1 – li * bi
for i = 1 to N-1
To solve this equation we first extend the equation LUx = D by y and say LUxy = Dy (y is not the solution vector of the very top equation). This can be separated to Ly = D and Ux = y.
Ly = D looks like
And resolving Ly = D gives
y1 * l1 + y2 = d2 y2 * l2 + y3 = d3 y3 * l3 + y4 = d4 |
=> y2 = d2 - y1 * l1 => y3 = d3 – y2 * l2 => y4 = d4 – y3 * l3 |
With this we basically have to implement 3 loops to solve the tridiagonal Matrix equation:
First loop:
m1 = a1
For i = 1 to n-1
put into c code:
m_lu[0] = m.a[0, 0];
for (i = 0; i < order - 1; i++)
{
if (Math.Abs(m_lu[i]) > 1E-10)
l_lu[i] = m[i + 1, i] / m_lu[i];
else
calcError = true;
m_lu[i + 1] = m[i + 1, i + 1] - l_lu[i] * m[i, i + 1];
}
y1 = d1
for i = 2 to n
In c code:
y_lu[0] =
m.y[0];
for (i = 1; i < order; i++)
{
y_lu[i] = y[i] - l_lu[i - 1] * y_lu[i - 1];
}
for (i = 1; i < order; i++)
{
y_lu[i] = y[i] - l_lu[i - 1] * y_lu[i - 1];
}
xn = yn / mn
For I = n-1 to 1
xi = (yi - bi * xi+1)/mi
In c code:
if (Math.Abs(m_lr[order - 1]) > 1E-10)
x[order - 1] = y_lu[order - 1] / m_lr[order - 1];
else
calcError = true;
if (!calcError)
{
for (i = order - 2; i >= 0; i--)
{
x[i] = (y_lu[i] - a[i, i + 1] * x[i + 1]) / m_lu[i];
}
}
m1 = a1
l1 = c2 / m1
m2 = a2 – l1 * b1
For i = 2 to n-1
li = ci+1 / mi
mi+1 = ai+1 – li * bi
yi = di – li-1 * yi-1
yN = dN – lN-1 * yN-1
So there remain 2 loops to solve the entire matrix equation and that takes a bit more than half as much calculation time as a Gaussian algorithm
The final c code :
m_lu[0] = m.a[0, 0];
for (i = 0; i < order - 1; i++)
{
if (Math.Abs(m_lu[i]) > 1E-10)
l_lu[i] = m[i + 1, i] / m_lu[i];
else
calcError = true;
m_lu[i + 1] = m[i + 1, i + 1] - l_lu[i] * m[i, i + 1];
}
y_lu[0] = m.y[0];
for (i = 1; i < order; i++)
{
y_lu[i] = y[i] - l_lu[i - 1] * y_lu[i - 1];
}
if (Math.Abs(m_lr[order - 1]) > 1E-10)
x[order - 1] = y_lu[order - 1] / m_lu[order - 1];
else
calcError = true;
if (!calcError)
{
for (i = order - 2; i >= 0; i--)
{
x[i] = (y_lu[i] - m[i, i + 1] * x[i + 1]) / m_lu[i];
}
}