package jhpro.interpolator;
import org.apache.commons.math3.analysis.interpolation.UnivariateInterpolator;
import org.apache.commons.math3.analysis.polynomials.PolynomialFunction;
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction;
import org.apache.commons.math3.exception.DimensionMismatchException;
import org.apache.commons.math3.exception.NonMonotonicSequenceException;
import org.apache.commons.math3.exception.NumberIsTooSmallException;
import org.apache.commons.math3.exception.util.LocalizedFormats;
import org.apache.commons.math3.util.MathArrays;
/**
* Constrained Spline Interpolation algorithm (CJC Kruger).
* If we want to avoid overshoot for an arbitrary function, we should find some other interpolation algorithm. One of the methods is called Constrained Cubic Spline Interpolation. It was proposed by CJC Kruger in his article: http://www.korf.co.uk/spline.pdf
*
* The algorithms is based on a classic cubic spline algorithm. The key step in it is the calculation of the slope (first derivative) at each point. Intuitively, the slope will be between the slopes of the adjacent straight lines (can be a mean value of the two slopes), but it also should approach zero if the slope of either line approaches zero.
*
* import android.util.Spline;
* float x[] = ...
* float y[[] = ...
* Spline f = Spline.createMonotoneCubicSpline(x, y);
*
// this will evaluate y0 = f(x0)
* float y0 = f.interpolate(x0);
*
*
* Created by Anton Danshin on 24/12/14.
*/
public class ConstrainedSplineInterpolator implements UnivariateInterpolator {
@Override
public PolynomialSplineFunction interpolate(double x[], double y[])
throws DimensionMismatchException, NumberIsTooSmallException, NonMonotonicSequenceException
{
if (x.length != y.length) {
throw new DimensionMismatchException(x.length, y.length);
}
if (x.length < 3) {
throw new NumberIsTooSmallException(LocalizedFormats.NUMBER_OF_POINTS,
x.length, 3, true);
}
// Number of intervals. The number of data points is n + 1.
final int n = x.length - 1;
MathArrays.checkOrder(x);
// Differences between knot points
final double dx[] = new double[n];
final double dy[] = new double[n];
for (int i = 0; i < n; i++) {
dx[i] = x[i + 1] - x[i];
dy[i] = y[i + 1] - y[i];
}
final double f1[] = new double[n+1]; // F'(x[i])
for (int i=1; i 0d) {
// doesn't change sign
f1[i] = 2d / (dx[i]/dy[i] + dx[i-1]/dy[i-1]);
} else if (slope <= 0d) {
// changes sign
f1[i] = 0d;
}
}
f1[0] = 3d * dy[0] / (2d * (dx[0])) - f1[1]/2d;
f1[n] = 3d * dy[n-1] / (2d * (dx[n-1])) - f1[n-1]/2d;
// cubic spline coefficients -- a contains constants, b is linear, c quadratic, d is cubic
final double a[] = new double[n+1];
final double b[] = new double[n+1];
final double c[] = new double[n+1];
final double d[] = new double[n+1];
for (int i = 1; i <= n; i++) {
final double f2a = - 2d * (f1[i] + 2 * f1[i-1]) / dx[i-1] + 6d * dy[i-1] / (dx[i-1]*dx[i-1]);
final double f2b = 2d * (2d * f1[i] + f1[i-1]) / dx[i-1] - 6d * dy[i-1] / (dx[i-1]*dx[i-1]);
d[i] = (f2b - f2a) / (6d * dx[i-1]);
c[i] = (x[i] * f2a - x[i-1] * f2b) / (2d * dx[i-1]);
b[i] = (dy[i-1] -
c[i] * (x[i]*x[i] - x[i-1]*x[i-1]) -
d[i] * (x[i]*x[i]*x[i] - x[i-1]*x[i-1]*x[i-1])
) / dx[i-1];
a[i] = y[i-1] - b[i]*x[i-1] - c[i]*x[i-1]*x[i-1] - d[i]*x[i-1]*x[i-1]*x[i-1];
}
final PolynomialFunction polynomials[] = new PolynomialFunction[n];
final double coefficients[] = new double[4];
for (int i = 1; i <= n; i++) {
coefficients[0] = a[i];
coefficients[1] = b[i];
coefficients[2] = c[i];
coefficients[3] = d[i];
final double x0 = x[i-1];
polynomials[i-1] = new PolynomialFunction(coefficients) {
@Override
public double value(double x) {
// bypass the standard Apache Commons behavior
return super.value(x + x0);
}
};
}
return new PolynomialSplineFunction(x, polynomials);
}
}
Ads help maintain this website.