Алгоритм построения интерполяционного кубического сплайна
Пусть каждому значению аргумента xi, i=0,...,n соответствуют значения функции f(xi)=yi и требуется найти функциональную зависимость в виде сплайна (1.1), удовлетворяющего перечисленным ниже требованиям:
а) функция S3(xi) непрерывна вместе со своими производными до второго порядка включительно;
б) S3(xi)=yi, i=0,1,...,n;
в) S"3(x0)=S"3(xn)=0.
Сформулированная выше задача имеет единственное решение.
Вторая производная S"3(x) непрерывна и, как видно из выражения (1.1), линейна на каждом отрезке [xi-1, xi], (i=1,...,n), поэтому представим ее в виде
, (1.4)
где hi = xi- xi-1 , mi= S"3(xi).
Интегрируя обе части равенства (1.4), получим
, (1.5)
где Ai и Bi - постоянные интегрирования.
Пусть в (1.5) x=xi и x=xi-1, тогда используя условия б), получим
, i=1,...,n.
Из этих уравнений находим Ai и Bi, и окончательно формула (1.5) принимает вид
. (1.6)
Из формулы (1.6) находим односторонние пределы производной в точках x1,x2 ,...,xn-1:
, (1.7)
. (1.8)
Приравнивая выражения (1.7) и (1.8) для i=1,...,n-1, получим n-1 уравнение
(1.9)
с n-1 неизвестными mi (i=1,...,n-1). Согласно условию (1.4) m0=mn=0.
Система линейных алгебраических уравнений (1.9) имеет трехдиагональную матрицу с диагональным преобладанием. Такие матрицы являются неособенными. Поэтому неизвестные m1 , m2 , ... , mn-1 находятся из системы (1.9) однозначно. Они могут быть найдены итерационными и прямыми методами решения систем линейных алгебраических уравнений, в том числе и методом прогонки. После определения mi функция S3(x) восстанавливается по формуле (1.6).
Метод прогонки
Пусть имеется система уравнений, записанная в матричном виде:
. (1.10)
В нашем случае согласно (1.9)
Решение системы ищется в виде
mi = i mi+1 + i , i=1,...,N-1, (1.11)
где Ai , Bi - прогоночные коэффициенты. Используя выражение для m i-1 из (1.11), исключим это неизвестное из i‑го уравнения системы. Получаем
(ai +ci i-1)mi + bi mi+1 = di -cii-1.
Сравнивая это соотношение с (1.11), выводим рекуррентные формулы для прогоночных коэффициентов i, i (прямая прогонка):
0=0=0, . (1.12)
Очевидно, что mn-1=n-1 (при сn-1=0). Все остальные неизвестные находим по формулам (1.11), используя выражения для прогоночных коэффициентов (1.12).
Для реализации алгоритма требуется выполнить 8(n-1) арифметических операций: 3(n-1) сложений, 3(n-1) умножений, 2(n-1) делений.
Величины i и ai +cii-1 не зависят от правой части системы. Поэтому если вычислить их и запомнить, то для решения систем, отличающихся только правыми частями, потребуется 5(n-1) арифметических операций.
Листинг программы:
#include <stdio.h>
#include <windows.h>
#include <iostream.h>
#include <stdlib.h>
float d[99999];
float l[99999];
float m[99999];
double f[99999];
float s[99999],mu[99999];
void main()
{
system("cls");
int n;
float c,a,b,shag;
float h;
cout<<"Vvedite shag h:=";
cin>>h;
cout<<"Vvedite kolichestvo znachenii n:=";
cin>>n;
shag=h/2;
c=h/6;
a=2*h/3;
b=h/6;
cout<<"c="<<c<<" a="<<a<<" b="<<b<<endl;
int i;
float x[99999],y[99999];
for(i=1;i<n+1;i++)
{
x[i]=(float)i;
}
for(i=1;i<n+1;i++)
{
cout<<"Vvedite Y["<<h*i<<"]:=";
cin>>y[i];
}
mu[0]=m[0]=m[n]=l[0]=0;
for(i=2;i<n+1;i++)
{
d[i]=((y[1+i]-y[i])/h)-(y[i]-y[i-1])/h;
}
for(i=1;i<n+1;i++)
{
l[i]=-b/(a+c*l[i-1]);
mu[i]=(d[i]-c*mu[i-1])/(a+c*l[i-1]);
}
for(i=n-1;i>1;i--)
{
m[i]=l[i]*m[i+1]+mu[i];
}
double po;
for(i=2;i<n;i++)
{
f[i]=x[i]-shag;
s[i]=(((x[i]-f[i])*y[i-1])/h)+(((f[i]-x[i-1])*y[i])/h)+(((x[i]-f[i])*(x[i]-f[i])*(x[i]-f[i]))-((h)*(h)*(x[i]-f[i]))*(m[i-1]))/(6*h)+((((f[i]-x[i-1])*(f[i]-x[i-1])*(f[i]-x[i-1]))-(h)*(h)*(f[i]-x[i-1]))*m[i])/(6*h);
po=(float)(h*i);
po=po-shag;
cout<<"S["<<po<<"]="<<s[i]<<endl;
po=(float)(h*i);
cout<<"S["<<po<<"]= "<<y[i]<<endl;
}
system("PAUSE");
}
Проверим правильность выполнения программы на примере функции y(x)=sinx, в качестве узловых точек возьмем точки из интервала (0;Π] с шагом h=Π/8. Найдем значения функции в промежуточных точках при помощи интерполяции функции сплайном:
Пример
Vvedite shag h:=0.3925
Vvedite kolichestvo znachenii n:=8
c=0.0654167 a=0.261667 b=0.0654167
Vvedite Y[0.3925]:=0.38249949727
Vvedite Y[0.785]:=0.7068251811
Vvedite Y[1.1775]:=0.92365
Vvedite Y[1.57]:=0.999999
Vvedite Y[1.9625]:=0.9246
Vvedite Y[2.355]:=0.70795
Vvedite Y[2.7475]:=0.38397
Vvedite Y[3.14]:=0.00159
S[0.58875]=1.49443
S[0.785]= 0.706825
S[0.98125]=2.10928
S[1.1775]= 0.92365
S[1.37375]=2.35328
S[1.57]= 0.999999
S[1.76625]=2.25135
S[1.9625]= 0.9246
S[2.15875]=1.80784
S[2.355]= 0.70795
S[2.55125]=1.087
S[2.7475]= 0.38397
Для продолжения нажмите любую клавишу . . .
Вывод: Как мы видим, значения функции, полученные нами в промежуточных точках, не совпадают с теоретическими значениями, это вызвано тем, что функция y(x)=sinx является периодической, что влечет собой достаточно большую погрешность при интерполировании.
Проверим также метод интерполяции на более монотонной функции, например на функции y(x)=x2. В качестве узловых точек возьмем точки из отрезка [1;8], с шагом h=1:
Vvedite shag h:=1
Vvedite kolichestvo znachenii n:=8
c=0.166667 a=0.666667 b=0.166667
Vvedite Y[1]:=1
Vvedite Y[2]:=4
Vvedite Y[3]:=9
Vvedite Y[4]:=16
Vvedite Y[5]:=25
Vvedite Y[6]:=36
Vvedite Y[7]:=49
Vvedite Y[8]:=64
S[1.5]=2.35101
S[2]= 4
S[2.5]=6.18125
S[3]= 9
S[3.5]=12.2424
S[4]= 16
S[4.5]=20.2242
S[5]= 25
S[5.5]=30.2359
S[6]= 36
S[6.5]=42.2074
S[7]= 49
Для продолжения нажмите любую клавишу . . .
Вывод: Как мы можем заметить, при интерполировании более монотонной функции, погрешность расчетов оказалось относительно небольшой.