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
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
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 .
Calcular las pendientes de las rectas secantes entre puntos sucesivos:
para .
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 .
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 .
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.
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
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
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. */functionDEBUG(s){/* Uncomment the following to enable verbose output of the solver: *///console.log(s);}varoutputCounter=0;varcreateInterpolant=function(xs,ys){vari,length=xs.length;// Deal with length issuesif(length!=ys.length){throw'Need an equal count of xs and ys.';}if(length===0){returnfunction(x){return0;};}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 numbersvarresult=+ys[0];returnfunction(x){returnresult;};}// Rearrange xs and ys so that xs is sortedvarindexes=[];for(i=0;i<length;i++){indexes.push(i);}indexes.sort(function(a,b){returnxs[a]<xs[b]?-1:1;});varoldXs=xs,oldYs=ys;// Impl: Creating new arrays also prevents problems if the input arrays are mutated laterxs=[];ys=[];// Impl: Unary plus properly converts values to numbersfor(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 slopesvardys=[],dxs=[],ms=[];for(i=0;i<length-1;i++){vardx=xs[i+1]-xs[i],dy=ys[i+1]-ys[i];dxs[i]=dx;dys[i]=dy;ms[i]=dy/dx;}// Get degree-1 coefficientsvarc1s=[ms[0]];for(i=0;i<dxs.length-1;i++){varm=ms[i],mNext=ms[i+1];if(m*mNext<=0){c1s[i]=0;}else{vardx_=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 coefficientsvarc2s=[],c3s=[];for(i=0;i<c1s.length-1;i++){varc1=c1s[i];varm_=ms[i];varinvDx=1/dxs[i];varcommon_=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 functionreturnfunction(x){// The rightmost point in the dataset should give an exact resultvari=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 xsvarlow=0,mid,high=c3s.length-1;while(low<=high){mid=Math.floor(0.5*(low+high));varxHere=xs[mid];if(xHere<x){low=mid+1;}elseif(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);// Interpolatevardiff=x-xs[i];outputCounter++;varinterpolatedValue=ys[i]+diff*(c1s[i]+diff*(c2s[i]+diff*c3s[i]));// The value of the interpolator's derivative at this point.varderivativeValue=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*/varX=[0,1,2,3,4];varF=[0,1,4,9,16];varf=createInterpolant(X,F);varN=X.length;console.log("# BLOCK 0 :: Data for monotone-cubic-spline.js");console.log("X"+"\t"+"F");for(vari=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 ");varmessage='';varM=25;for(vari=0;i<=M;i+=1){varx=X[0]+(X[N-1]-X[0])*i/M;varrvals=f(x);varP=rvals[0];varD=rvals[1];message+=x.toPrecision(15)+'\t'+P.toPrecision(15)+'\t'+D.toPrecision(15)+'\n';}console.log(message);