Apache Commons Math tiene una bonita serie de algoritmos, en particular "SplineInterpolator", ver el API docs
Un ejemplo en el que llamamos las funciones de interpolación para alpha (x), beta (x) a partir de Groovy:
package example.com
import org.apache.commons.math3.analysis.interpolation.SplineInterpolator
import org.apache.commons.math3.analysis.polynomials.PolynomialSplineFunction
import statec.Extrapolate.Value;
class Interpolate {
enum Value {
ALPHA, BETA
}
def static xValues = [
-284086,
-94784,
31446,
354837,
667782,
982191
]
def static alphaValues = [
71641,
78245,
80871,
94045,
105780,
119616
]
def static betaValues = [
95552,
103413,
108667,
128456,
144686,
171953
]
static def getValueByName(Value value, int i) {
def res
switch (value) {
case Value.ALPHA:
res = alphaValues[i]
break
case Value.BETA:
res = betaValues[i]
break
default:
assert false
}
return res
}
static PolynomialSplineFunction interpolate(Value value) {
def yValues = []
int i = 0
xValues.each {
def y = getValueByName(value, i++)
yValues << (y as Double)
}
SplineInterpolator spi = new SplineInterpolator()
return spi.interpolate(xValues as double[], yValues as double[])
}
static void main(def argv) {
//
// Create a map mapping a Value instance to its interpolating function
//
def interpolations = [:]
Value.values().each {
interpolations[it] = interpolate(it)
}
//
// Create an array of new x values to compute display.
// Make sure the last "original" value is in there!
// Note that the newxValues MUST stay within the range of the original xValues!
//
def newxValues = []
for (long x = xValues[0] ; x < xValues[-1] ; x+=25000) {
newxValues << x
}
newxValues << xValues[-1]
//
// Write interpolated values for ALPHA and BETA, adding the original values in columns 4 and 5
//
System.out << "X , ALPHA, BETA, X_orig, ALPHA_orig, BETA_orig" << "\n"
int origIndex = 0
newxValues.each { long x ->
def alpha_ipol = interpolations[Value.ALPHA].value(x)
def beta_ipol = interpolations[Value.BETA].value(x)
String out = "${x} , ${alpha_ipol} , ${beta_ipol}"
if (x >= xValues[origIndex]) {
out += ", ${xValues[origIndex]}, ${alphaValues[origIndex]}, ${betaValues[origIndex]}"
origIndex++
}
System.out << out << "\n"
}
}
}
Y ahora para un ejemplo fuera de tema para las extrapolaciones, porque es divertido. Aquí usamos los mismos datos que antes, pero extrapolamos utilizando un polinomio de segundo grado. Y las clases apropiadas, por supuesto. Una vez más, en el maravilloso:
package example.com
import org.apache.commons.math3.analysis.polynomials.PolynomialFunction
import org.apache.commons.math3.fitting.PolynomialFitter
import org.apache.commons.math3.fitting.WeightedObservedPoint
import org.apache.commons.math3.optim.SimpleVectorValueChecker
import org.apache.commons.math3.optim.nonlinear.vector.jacobian.GaussNewtonOptimizer
class Extrapolate {
enum Value {
ALPHA, BETA
}
def static xValues = [
-284086,
-94784,
31446,
354837,
667782,
982191
]
def static alphaValues = [
71641,
78245,
80871,
94045,
105780,
119616
]
def static betaValues = [
95552,
103413,
108667,
128456,
144686,
171953
]
static def getValueByName(Value value, int i) {
def res
switch (value) {
case Value.ALPHA:
res = alphaValues[i]
break
case Value.BETA:
res = betaValues[i]
break
default:
assert false
}
return res
}
static PolynomialFunction extrapolate(Value value) {
//
// how to check that we converged
//
def checker
A: {
double relativeThreshold = 0.01
double absoluteThreshold = 10
int maxIter = 1000
checker = new SimpleVectorValueChecker(relativeThreshold, absoluteThreshold, maxIter)
}
//
// how to fit
//
def fitter
B: {
def useLUdecomposition = true
def optimizer = new GaussNewtonOptimizer(useLUdecomposition, checker)
fitter = new PolynomialFitter(optimizer)
int i = 0
xValues.each {
def weight = 1.0
def y = getValueByName(value, i++)
fitter.addObservedPoint(new WeightedObservedPoint(weight, it, y))
}
}
//
// fit using a 2-degree polynomial; guess at a linear function at first
// "a0 + (a1 * x) + (a2 * x²)"; a linear guess mean a2 == 0
//
def params
C: {
def mStart = getValueByName(value,0)
def mEnd = getValueByName(value,-1)
def xStart = xValues[0]
def xEnd = xValues[-1]
def a2 = 0
def a1 = (mEnd - mStart)/(xEnd - xStart) // slope
def a0 = mStart - (xStart * a1) // 0-intersection
def guess = [a0 , a1 , a2]
params = fitter.fit(guess as double[])
}
//
// make polynomial
//
return new PolynomialFunction(params)
}
static void main(def argv) {
//
// Create a map mapping a Value instance to its interpolating function
//
def extrapolations = [:]
Value.values().each {
extrapolations[it] = extrapolate(it)
}
//
// New x, this times reaching out past the range of the original xValues
//
def newxValues = []
for (long x = xValues[0] - 400000L ; x < xValues[-1] + 400000L ; x += 10000) {
newxValues << x
}
//
// Write the extrapolated series ALPHA and BETA, adding the original values in columns 4 and 5
//
System.out << "X , ALPHA, BETA, X_orig, ALPHA_orig, BETA_orig" << "\n"
int origIndex = 0
newxValues.each { long x ->
def alpha_xpol = extrapolations[Value.ALPHA].value(x)
def beta_xpol = extrapolations[Value.BETA].value(x)
String out = "${x} , ${alpha_xpol} , ${beta_xpol}"
if (origIndex < xValues.size() && x >= xValues[origIndex]) {
out += ", ${xValues[origIndex]}, ${alphaValues[origIndex]}, ${betaValues[origIndex]}"
origIndex++
}
System.out << out << "\n"
}
}
}
ser la pregunta "en espera": Se podría reformular la pregunta para dar ejemplo de código en Java de la curva Código de conexión (por supuesto que el código se tire en algunas bibliotecas, por lo uno puede ver eso como una recomendación). Esta pregunta no es sobre JFreeChart, que simplemente toma los puntos y LOS VISUALIZA, pero NO GENERA puntos adicionales. De hecho, me sorprende que Linked and Related no muestre exactamente esa pregunta. –