|
|
Line 1: |
Line 1: |
| In the [[mathematics|mathematical]] subfield of [[numerical analysis]], '''monotone cubic interpolation''' is a variant of [[cubic interpolation]] that preserves [[Monotone function|monotonicity]] of the [[data set]] being interpolated.
| | Nice to satisfy you, my name is Ling and I completely dig that title. What she enjoys doing is taking part in croquet and she is attempting to make it a occupation. He currently lives in Idaho and his mothers and fathers live nearby. She is currently a cashier but quickly she'll be on her personal.<br><br>Also visit my blog: [http://forsaken-Ranger.5Cz.de/index.php?mod=users&action=view&id=1945 car warranty] |
| | |
| Monotonicity is preserved by [[linear interpolation]] but not guaranteed by [[cubic interpolation]].
| |
| | |
| ==Monotone cubic Hermite interpolation==
| |
| [[Image:MonotCubInt.png|thumb|300px|right|Example showing non-monotone cubic interpolation (in red) and monotone cubic interpolation (in blue) of a monotone data set.]]
| |
| Monotone interpolation can be accomplished using [[cubic Hermite spline]] with the tangents <math>m_i</math> modified to ensure the monotonicity of the resulting Hermite spline.
| |
| | |
| ===Interpolant selection===
| |
| | |
| There are several ways of selecting interpolating tangents for each data point. This section will outline the use of the Fritsch–Carlson method.
| |
| | |
| Let the data points be <math>(x_k,y_k)</math> for <math>k=1,...,n</math>
| |
| # Compute the slopes of the [[secant line]]s between successive points:<blockquote><math>\Delta_k =\frac{y_{k+1}-y_k}{x_{k+1}-x_k}</math></blockquote> for <math>k=1,\dots,n-1</math>.
| |
| # Initialize the tangents at every data point as the average of the secants, <blockquote><math>m_k = \frac{\Delta_{k-1}+\Delta_k}{2}</math></blockquote> for <math>k=2,\dots,n-1</math>; these may be updated in further steps. For the endpoints, use one-sided differences: <blockquote><math>m_1 = \Delta_1 \quad \text{and} \quad m_n = \Delta_{n-1}</math></blockquote>
| |
| # For <math>k=1,\dots,n-1</math>, if <math>\Delta_k = 0</math> (if two successive <math>y_k=y_{k+1}</math> are equal), then set <math>m_k = m_{k+1} = 0,</math> as the spline connecting these points must be flat to preserve monotonicity. Ignore step 4 and 5 for those <math>k</math>.
| |
| # Let <math>\alpha_k = m_k/\Delta_k</math> and <math>\beta_k = m_{k+1}/\Delta_k</math>. If <math>\alpha_k</math> or <math>\beta_{k-1}</math> are computed to be less than zero, then the input data points are not strictly monotone, and <math>(x_k,y_k)</math> is a local extremum. In such cases, piecewise monotone curves can still be generated by choosing <math>m_{k}=0</math>, although global strict monotonicity is not possible.
| |
| # To prevent [[overshoot]] and ensure monotonicity, at least one of the following conditions must be met:
| |
| ##the function <blockquote><math>\phi(\alpha, \beta) = \alpha - \frac{(2 \alpha + \beta - 3)^2}{3(\alpha + \beta - 2)}</math></blockquote> must have a value greater than or equal to zero;
| |
| ##<math>\alpha + 2\beta - 3 \le 0</math>; or
| |
| ##<math>2\alpha + \beta - 3 \le 0</math>.
| |
| If monotonicity must be strict then <math>\phi(\alpha, \beta)</math> must have a value strictly greater than zero.
| |
| | |
| One simple way to satisfy this constraint is to restrict the magnitude of vector <math>(\alpha_k, \beta_k)</math> to a circle of radius 3. That is, if <math>\alpha_k^2 + \beta_k^2 > 9</math>, then set <math>m_k = \tau_k \alpha_k \Delta_k</math> and <math>m_{k+1} = \tau_k \beta_k \Delta_k</math> where <math>\tau_k = \frac{3}{\sqrt{\alpha_k^2 + \beta_k^2}}</math>.
| |
| | |
| Alternatively it is sufficient to restrict <math>\alpha_k \le 3</math> and <math>\beta_k \le 3</math>. To accomplish this if <math>\alpha_k > 3</math>, then set <math>m_k = 3\times \Delta_k</math>. Similarly for <math>\beta</math>.
| |
| | |
| Note that only one pass of the algorithm is required.
| |
| | |
| ===Cubic interpolation===
| |
| | |
| After the preprocessing, evaluation of the interpolated spline is equivalent to [[cubic Hermite spline]], using the data <math>x_k</math>, <math>y_k</math>, and <math>m_k</math> for <math>k=1,...,n</math>.
| |
| | |
| To evaluate at <math>x</math>, find the smallest value larger than <math>x</math>, <math>x_\text{upper}</math>, and the largest value smaller than <math>x</math>, <math>x_\text{lower}</math>, among <math>x_k</math> such that <math>x_\text{lower} \leq x \leq x_\text{upper}</math>. Calculate
| |
| :<math>h = x_\text{upper}-x_\text{lower}</math> and <math>t = \frac{x - x_\text{lower}}{h}</math> | |
| then the interpolant is
| |
| :<math>f_\text{interpolated}(x) = y_\text{lower} h_{00}(t) + h m_\text{lower} h_{10}(t) + y_\text{upper} h_{01}(t) + h m_\text{upper}h_{11}(t)</math>
| |
| where <math>h_{ii}</math> are the basis functions for the [[cubic Hermite spline]].
| |
| | |
| ==Example implementation==
| |
| The following [[JavaScript]] implementation takes a data set and produces a Fritsch-Carlson cubic spline interpolant function:
| |
| <syntaxhighlight lang="javascript">
| |
| /* Fritsch-Carlson monotone cubic spline interpolation
| |
| Usage example:
| |
| var f = createInterpolant([0, 1, 2, 3], [0, 1, 4, 9]);
| |
| var message = '';
| |
| for (var x = 0; x <= 3; x += 0.5) {
| |
| var xSquared = f(x);
| |
| message += x + ' squared is about ' + xSquared + '\n';
| |
| }
| |
| alert(message);
| |
| */
| |
| 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.push(+oldXs[indexes[i]]); ys.push(+oldYs[indexes[i]]); }
| |
|
| |
| // 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.push(dx); dys.push(dy); ms.push(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.push(0);
| |
| } else {
| |
| var dx = dxs[i], dxNext = dxs[i + 1], common = dx + dxNext;
| |
| c1s.push(3*common/((common + dxNext)/m + (common + dx)/mNext));
| |
| }
| |
| }
| |
| c1s.push(ms[ms.length - 1]);
| |
|
| |
| // Get degree-2 and degree-3 coefficients
| |
| var c2s = [], c3s = [];
| |
| for (i = 0; i < c1s.length - 1; i++) {
| |
| var c1 = c1s[i], m = ms[i], invDx = 1/dxs[i], common = c1 + c1s[i + 1] - m - m;
| |
| c2s.push((m - c1 - common)*invDx); c3s.push(common*invDx*invDx);
| |
| }
| |
|
| |
| // Return interpolant function
| |
| return function(x) {
| |
| // The rightmost point in the dataset should give an exact result
| |
| var i = xs.length - 1;
| |
| 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 { return ys[mid]; }
| |
| }
| |
| i = Math.max(0, high);
| |
|
| |
| // Interpolate
| |
| var diff = x - xs[i], diffSq = diff*diff;
| |
| return ys[i] + c1s[i]*diff + c2s[i]*diffSq + c3s[i]*diff*diffSq;
| |
| };
| |
| };
| |
| </syntaxhighlight>
| |
| | |
| ==References== | |
| *{{cite journal
| |
| | last = Fritsch
| |
| | first = F. N.
| |
| | coauthors = Carlson, R. E.
| |
| | title = Monotone Piecewise Cubic Interpolation
| |
| | journal = [[SIAM Journal on Numerical Analysis]]
| |
| | volume = 17
| |
| | issue = 2
| |
| | pages = 238–246
| |
| | publisher = SIAM
| |
| | date = 1980
| |
| | doi = 10.1137/0717021
| |
| }}
| |
| | |
| ==External links==
| |
| * [[GPL]]v3 licensed [[C++]] implementation: [https://github.com/OPM/opm-core/blob/master/opm/core/utility/MonotCubicInterpolator.cpp MonotCubicInterpolator.cpp] [https://github.com/OPM/opm-core/blob/master/opm/core/utility/MonotCubicInterpolator.hpp MonotCubicInterpolator.hpp]
| |
| | |
| [[Category:Interpolation]]
| |
| [[Category:Splines]]
| |
Nice to satisfy you, my name is Ling and I completely dig that title. What she enjoys doing is taking part in croquet and she is attempting to make it a occupation. He currently lives in Idaho and his mothers and fathers live nearby. She is currently a cashier but quickly she'll be on her personal.
Also visit my blog: car warranty