Interpolación cúbica monótona

variante de la interpolación cúbica que preserva la monotonicidad del conjunto de datos que se interpola

En el campo matemático del análisis numérico, la interpolación cúbica monótona es una variante del interpolador cúbico de Hermite que preserva la monotonicidad del conjunto de datos que se está interpolando.[1]

Ejemplo que muestra la interpolación cúbica no monótona (en rojo) y la interpolación cúbica monótona (en azul) de un conjunto de datos monótonos

La interpolación lineal preserva la monotonicidad (es decir, que la altura de la serie de puntos y de la curva sea siempre ascendente o descendente, sin cambios en el signo de la pendiente), pero la interpolación cúbica no la garantiza.

Interpolación cúbica monótona de Hermite editar

La interpolación monótona se puede lograr usando el interpolador cúbico de Hermite con las tangentes   modificadas para garantizar la monotonicidad del spline de Hermite resultante.

También está disponible un algoritmo para la interpolación monótona quíntica de Hermite.

Selección del polinomio interpolante editar

Hay varias formas de seleccionar tangentes de interpolación para cada punto del conjunto de datos. Esta sección describirá el uso del método de Fritsch-Carlson.[2]​ Téngase en cuenta que solo se requiere una pasada del algoritmo.

Considérese que los puntos del conjunto de datos   se indexen en orden para  .

  1. Calcular las pendientes de las rectas secantes entre puntos sucesivos:

     

    para  .

  2. Estas asignaciones son provisionales, y pueden ser reemplazadas en los pasos restantes. Inicialícense las tangentes en cada punto de datos interior como el promedio de las secantes,

     

    para  .

    Para los puntos finales, utilizar las diferencias de un solo lado:

     .

    Si   y   tienen signos opuestos, configurar  .

  3. Para  , siempre que   (donde dos   sucesivos sean iguales),
    establecer   ya que el spline que conecta estos puntos siempre debe decrecer (o crecer) a medida que se avanza según el eje x para preservar la monotonicidad.
    Ignorar los pasos 4 y 5 para aquellos  .

  4. Dejar

     .

    Si   o   es negativo, entonces los puntos de datos de entrada no son estrictamente monótonos y   es un extremo local. En tales casos, aún se pueden generar curvas monótonas "por partes" eligiendo   si   o   si  , aunque la monotonicidad estricta no es posible globalmente.

  5. Para evitar sobrepaso y garantizar la monotonicidad, se debe cumplir al menos una de las tres condiciones siguientes:
(a) La función

 , 'o

(b)  , 'o
(c)  .
Solo la condición (a) es suficiente para garantizar una monotonicidad estricta:   debe ser positivo.

Una forma sencilla de satisfacer esta restricción es restringir el vector   a un círculo de radio 3. Es decir, si es  , establecer

 ,

y cambiar la escala de las tangentes mediante

 .

Como alternativa, es suficiente restringir   y  . Para lograr esto, si es  , configurar  .

Interpolación cúbica editar

Después del preprocesamiento anterior, la evaluación del spline interpolado es equivalente a la del interpolador cúbico de Hermite, utilizando los datos  ,   y   para  .

Para evaluar en  , buscar el índice   en la secuencia donde   se encuentra entre   y  , es decir:  . Calcular

 

entonces, el valor interpolado es

 

donde   son las funciones básicas para el interpolador cúbico de Hermite.

Código de ejemplo editar

El siguiente código en JavaScript toma un conjunto de datos y produce una función de interpolación monótona mediante un spline cúbico:

/*
 * Monotone cubic spline interpolation
 * Usage example listed at bottom; this is a fully-functional package. For
 * example, this can be executed either at sites like
 * https://www.programiz.com/javascript/online-compiler/
 * or using nodeJS.
 */
function DEBUG(s) {
    /* Uncomment the following to enable verbose output of the solver: */
    //console.log(s);
}
var outputCounter= 0;
var createInterpolant= function(xs, ys) {
    var i, length= xs.length;
    
    // Deal with length issues
    if (length != ys.length) {throw 'Need an equal count of xs and ys.'; }
    if (length=== 0) {return function(x) {return 0; }; }
    if (length=== 1) {
        // Impl: Precomputing the result prevents problems if ys is mutated later and allows garbage collection of ys
        // Impl: Unary plus properly converts values to numbers
        var result= +ys[0];
        return function(x) {return result; };
    }
    
    // Rearrange xs and ys so that xs is sorted
    var indexes= [];
    for (i= 0; i < length; i++) {indexes.push(i); }
    indexes.sort(function(a, b) {return xs[a] < xs[b] ? -1 : 1; });
    var oldXs= xs, oldYs= ys;
    // Impl: Creating new arrays also prevents problems if the input arrays are mutated later
    xs= []; ys= [];
    // Impl: Unary plus properly converts values to numbers
    for (i= 0; i < length; i++) {
        xs[i]= +oldXs[indexes[i]];
        ys[i]= +oldYs[indexes[i]];
    }

    DEBUG("debug: xs= [ " + xs + " ]")
    DEBUG("debug: ys= [ " + ys + " ]")
    
    // Get consecutive differences and slopes
    var dys= [], dxs= [], ms= [];
    for (i= 0; i < length - 1; i++) {
        var dx= xs[i + 1] - xs[i], dy= ys[i + 1] - ys[i];
        dxs[i]= dx;
        dys[i]= dy;
        ms[i]= dy/dx;
    }
    // Get degree-1 coefficients
    var c1s= [ms[0]];
    for (i= 0; i < dxs.length - 1; i++) {
        var m= ms[i], mNext= ms[i + 1];
        if (m*mNext <= 0) {
            c1s[i]= 0;
        } else {
            var dx_= dxs[i], dxNext= dxs[i + 1], common= dx_ + dxNext;
            c1s[i]= 3*common/((common + dxNext)/m + (common + dx_)/mNext);
        }
    }
    c1s.push(ms[ms.length - 1]);

    DEBUG("debug: dxs= [ " + dxs + " ]")
    DEBUG("debug: ms= [ " + ms + " ]")
    DEBUG("debug: c1s.length= " + c1s.length)
    DEBUG("debug: c1s= [ " + c1s + " ]")
    
    // Get degree-2 and degree-3 coefficients
    var c2s= [], c3s= [];
    for (i= 0; i < c1s.length - 1; i++) {
        var c1= c1s[i];
        var m_= ms[i];
        var invDx= 1/dxs[i];
        var common_= c1 + c1s[i + 1] - m_ - m_;
        DEBUG("debug: " + i + ". c1= " + c1);
        DEBUG("debug: " + i + ". m_= " + m_);
        DEBUG("debug: " + i + ". invDx= " + invDx);
        DEBUG("debug: " + i + ". common_= " + common_);
        c2s[i]= (m_ - c1 - common_)*invDx;
        c3s[i]= common_*invDx*invDx;
    }
    DEBUG("debug: c2s= [ " + c2s + " ]")
    DEBUG("debug: c3s= [ " + c3s + " ]")

    // Return interpolant function
    return function(x) {
        // The rightmost point in the dataset should give an exact result
        var i= xs.length - 1;
        // Uncomment the following to return only the interpolated value.
        //if (x== xs[i]) {return ys[i]; }
        
        // Search for the interval x is in, returning the corresponding y if x is one of the original xs
        var low= 0, mid, high= c3s.length - 1;
        while (low <= high) {
            mid= Math.floor(0.5*(low + high));
            var xHere= xs[mid];
            if (xHere < x) {low= mid + 1; }
            else if (xHere > x) {high= mid - 1; }
            else {
                // Uncomment the following to return only the interpolated value.
                //return ys[mid];
                low= c3s.length - 1;
                high= mid;
                break;
            }
        }
        i= Math.max(0, high);

        // Interpolate
        var diff= x - xs[i];
        outputCounter++;
        var interpolatedValue= ys[i] + diff * (c1s[i] + diff *  (c2s[i] + diff * c3s[i]));
        // The value of the interpolator's derivative at this point.
        var derivativeValue= c1s[i] + diff * (2*c2s[i] + diff * 3*c3s[i]);
        DEBUG("debug: #" + outputCounter + ". x= " + x + ". i= " + i + ", diff= " + diff + ", interpolatedValue= " + interpolatedValue + ", derivativeValue= " + derivativeValue);
        // Uncomment the following to return only the interpolated value.
        // return interpolatedValue;
        return [ interpolatedValue, derivativeValue ];
    };
};

/*
   Usage example below will approximate x^2 for 0 <= x <= 4.

   Command line usage example (requires installation of nodejs):
   node monotone-cubic-spline.js
*/

var X= [0, 1, 2, 3, 4];
var F= [0, 1, 4, 9, 16];
var f= createInterpolant(X,F);
var N= X.length;
console.log("# BLOCK 0 :: Data for monotone-cubic-spline.js");
console.log("X" + "\t" + "F");
for (var i= 0; i < N; i += 1) {
    console.log(X[i] + '\t' + F[i]);
}
console.log(" ");
console.log(" ");
console.log("# BLOCK 1 :: Interpolated data for monotone-cubic-spline.js");
console.log("      x       " + "\t\t" + "     P(x)      " + "\t\t" + "    dP(x)/dx     ");
var message= '';
var M= 25;
for (var i= 0; i <= M; i += 1) {
    var x= X[0] + (X[N-1]-X[0])*i/M;
    var rvals= f(x);
    var P= rvals[0];
    var D= rvals[1];
    message += x.toPrecision(15) + '\t' + P.toPrecision(15) + '\t' + D.toPrecision(15) + '\n';
}
console.log(message);

Referencias editar

  1. Gary D. Knott (2012). Interpolating Cubic Splines. Springer Science & Business Media. pp. 69 de 244. ISBN 9781461213208. Consultado el 23 de enero de 2024. 
  2. Boris I Kvasov (2000). Methods Of Shape-preserving Spline Approximation. World Scientific. pp. 146 de 356. ISBN 9789814494472. Consultado el 23 de enero de 2024. 

Bibliografía editar

Enlaces externos editar