Anar al contingut

Interpolació cúbica monòtona

De L'Enciclopèdia, la wikipedia en valencià
Archiu:MonotCubInt es.png
Eixemple que mostra l'interpolació cúbica no monòtona (en roig) i l'interpolació cúbica monòtona (en blava) d'un conjunt de senyes monòtones

En el camp matemàtic del anàlisis numèric, la interpolació cúbica monòtona és una variant del interpolador cúbic de Hermite que preserva la monotonicidad del conjunt de senyes que s'està interpolando.[1]

l'interpolació llineal preserva la monotonicidad (és dir, que l'altura de la série de punts i de la curva siga sempre ascendent o descendent, sense canvis en el signe de la pendent), pero l'interpolació cúbica no la garantisa.

Interpolació cúbica monòtona de Hermite

[editar | editar còdic]

L'interpolació monòtona es pot conseguir usant l'interpolador cúbic de Hermite en les tangentes mi modificades per a garantisar la monotonicidad del spline de Hermite resultant.

També està disponible un algoritme per a l'interpolació monòtona quíntica de Hermite.

Selecció del polinomi interpolante

[editar | editar còdic]

Hi ha vàries formes de seleccionar tangentes d'interpolació per a cada punt del conjunt de senyes. Esta secció descriurà l'us del método de Fritsch-Carlson.[2] Tinga's en conte que solament es requerix una passada de l'algoritme.

Considere's que els punts del conjunt de senyes (xk,yk) s'indexen en orde per a k=1,n.

  1. Calcular les pendents de les rectes secantes entre punts successius:
    δk=yk+1ykxk+1xk
    per a k=1,n1.

  2. Estes assignació són provisionals, i poden ser reemplaçades en els passos restants. Inicialícense les tangentes en cada punt de senyes interior com el promig de les secantes,
    mk=δk1+δk2
    per a k=2,n1.

    Per als punts finals, utilisar les diferències d'un sol costat:
    m1=δ1 y mn=δn1

    .

    Si δk1 i δk tenen signes oposts, configurar mk=0.

  3. Per a k=1,n1, sempre que δk=0 (a on dos yk=yk+1 successius siguen iguals),
    establir mk=mk+1=0, ya que el spline que conecta estos punts sempre deu decréixer (o créixer) a mida que s'alvança segons l'eix x per a preservar la monotonicidad.
    Ignorar els passos 4 i 5 per a aquells k.

  4. Deixar
    αk=mk/δk y βk=mk+1/δk

    .

    Si αk o βk és negatiu, llavors els punts de senyes d'entrada no són estrictament monòtons i (xk,yk) és un extrem local. En tals casos, encara es poden generar curves monòtones "per parts" elegint mk=0 si αk<0 o mk+1=0 si βk<0, encara que la monotonicidad estricta no és possible globalment.

  5. Per a evitar sobrepasse i garantisar la monotonicidad, es deu complir a lo manco una de les tres condicions següents:
(a) La funció
ϕk=αk(2αk+βk3)23(αk+βk2)>0

, 'o

(b) αk+2βk30, 'o
(c) 2αk+βk30.
Solament la condició (a) és suficient per a garantisar una monotonicidad estricta: ϕk deu ser positiu.

Una forma senzilla de satisfer esta restricció és restringir el vector (αk,βk) a un círcul de radi 3. És dir, si és αk2+βk2>9, establir
τk=3αk2+βk2

,

i canviar l'escala de les tangentes per mig de
mk=τkαkδk y mk+1=τkβkδk

.

Com a alternativa, és suficient restringir αk3 i βk3. Per a conseguir açò, si és αk>3 o βk>3, configurar mk=3δk.

Interpolació cúbica

[editar | editar còdic]

Despuix del preprocesamiento anterior, l'evaluació del spline interpolado és equivalent a la del interpolador cúbic de Hermite, utilisant les senyes xk, yk i mk per a k=1,n.

Per a evaluar en x, buscar l'índex k en la seqüència a on x es troba entre xk i xk+1, és dir: xkxxk+1. Calcular

Δ=xk+1xk y t=xxkΔ

llavors, el valor interpolado és

f interpolada (x)=ykh00(t)+Δmkh10(t)+yk+1h01(t)+Δmk+1h11(t)

a on hii són les funcions bàsiques per al interpolador cúbic de Hermite.

Còdic d'eixemple

[editar | editar còdic]

El següent còdic en JavaScript pren un conjunt de senyes i produïx una funció d'interpolació monòtona per mig d'un spline cúbic:

/*
 * Monotone cubic spline interpolation
 * Usage example listed at bottom; this is a fully-functional package. For
 * example, this ca 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 baix 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 llaure 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 i 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 * (2c2s[i] + diff * 3c3s[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" + "	" + "F");
for (var i= 0; i < N; i += 1) {
    console.log(X[i] + '	' + F[i]);
}
console.log(" ");
console.log(" ");
console.log("# BLOCK 1 :: Interpolated data for monotone-cubic-spline.js");
console.log("      x       " + "		" + "     P(x)      " + "		" + "    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) + '	' + P.toPrecision(15) + '	' + D.toPrecision(15) + '
';
}
console.log(message);

Referències

[editar | editar còdic]
  1. Gary D. Knott (2012). Interpolating Cubic Splines, Springer Science & Business Media, pp. 69 de 244. ISBN 9781461213208.
  2. Boris I Kvasov (2000). Methods Of Shape-preserving Spline Approximation, World Scientific, pp. 146 de 356. ISBN 9789814494472.

Bibliografia

[editar | editar còdic]


Referències

[editar | editar còdic]