Монотонная кубическая интерполяция
В математической области анализа численного монотонная кубическая интерполяция — это вариант кубической интерполяции , который сохраняет монотонность набора данных интерполируемого .
Монотонность сохраняется при линейной интерполяции , но не гарантируется кубической интерполяцией .
Монотонная кубическая интерполяция Эрмита
[ редактировать ]
Монотонную интерполяцию можно выполнить с помощью кубического сплайна Эрмита с касательными. модифицирован для обеспечения монотонности полученного сплайна Эрмита.
Также доступен алгоритм монотонной пятерной интерполяции Эрмита.
Интерполянтный выбор
[ редактировать ]Существует несколько способов выбора интерполирующих касательных для каждой точки данных. В этом разделе будет описано использование метода Фрича – Карлсона. Обратите внимание, что требуется только один проход алгоритма.
Пусть точки данных будут индексируется в отсортированном порядке для .
- Вычислите наклоны секущих между последовательными точками:
для . - Эти назначения являются предварительными и могут быть заменены на оставшихся этапах. Инициализируйте касательные в каждой внутренней точке данных как среднее значение секущих,
для .
Для конечных точек используйте односторонние разности:
Если и имеют противоположные знаки, множество ..
- Для , где бы то ни было (где когда-либо два последовательных равны),
набор поскольку сплайн, соединяющий эти точки, должен быть плоским, чтобы сохранить монотонность.
Игнорируйте шаги 4 и 5 для тех, . - Позволять
Если либо или отрицательно, то точки входных данных не являются строго монотонными и является локальным экстремумом. В таких случаях кусочно -монотонные кривые все же можно получить, выбрав если или если , хотя строгая монотонность в глобальном масштабе невозможна..
- Чтобы предотвратить перерегулирование и обеспечить монотонность, должно быть выполнено хотя бы одно из следующих трех условий:
- (а) функция
, или
- (б) , или
- (с) .
- Только условия (а) достаточно для обеспечения строгой монотонности: должен быть положительным.
- Один простой способ удовлетворить это ограничение — ограничить вектор к окружности радиуса 3. То есть, если , затем установите
и измените масштаб касательных через,
.
- В качестве альтернативы достаточно ограничить и . Для этого, если , затем установите , и если , затем установите .
- (а) функция
Кубическая интерполяция
[ редактировать ]После предварительной обработки, описанной выше, оценка интерполированного сплайна эквивалентна кубическому сплайну Эрмита с использованием данных , , и для .
Чтобы оценить на , найдите индекс в той последовательности, где , лежит между , и , то есть: . Рассчитать
тогда интерполированное значение равно
где являются базисными функциями кубического сплайна Эрмита .
Пример реализации
[ редактировать ]Следующая реализация JavaScript принимает набор данных и создает монотонную интерполянтную функцию кубического сплайна:
/*
* 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 j = 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.push(+oldXs[indexes[i]]); ys.push(+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.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]);
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.push((m_ - c1 - common_)*invDx);
c3s.push(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;
//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, rval, dval;
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 {
j++;
i = mid;
var diff = x - xs[i];
rval = ys[i] + diff * (c1s[i] + diff * (c2s[i] + diff * c3s[i]));
dval = c1s[i] + diff * (2*c2s[i] + diff * 3*c3s[i]);
DEBUG("debug: " + j + ". x = " + x + ". i = " + i + ", diff = " + diff + ", rval = " + rval + ", dval = " + dval);
return [ rval, dval ];
}
}
i = Math.max(0, high);
// Interpolate
var diff = x - xs[i];
j++;
rval = ys[i] + diff * (c1s[i] + diff * (c2s[i] + diff * c3s[i]));
dval = c1s[i] + diff * (2*c2s[i] + diff * 3*c3s[i]);
DEBUG("debug: " + j + ". x = " + x + ". i = " + i + ", diff = " + diff + ", rval = " + rval + ", dval = " + dval);
return [ rval, dval ];
};
};
/*
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(F[i] + '\t' + X[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);
Ссылки
[ редактировать ]- Фрич, ФН; Карлсон, Р.Э. (1980). «Монотонная кусочно-кубическая интерполяция». SIAM Journal по численному анализу . 17 (2). СИАМ: 238–246. дои : 10.1137/0717021 .
- Догерти, РЛ; Эдельман, А.; Хайман, Дж. М. (апрель 1989 г.). «Кубическая и пятая интерполяция Эрмита, сохраняющая положительность, монотонность или выпуклость» . Математика вычислений . 52 (186): 471–494. дои : 10.2307/2008477 .