2011-10-04 5 views
6

Я пытаюсь написать программу интерполяции кубических сплайнов. Я написал программу, но график не получается правильно. Сплайн использует естественные граничные условия (второй дернатив в начале/концевом узле равен 0). Код находится в Matlab, и будет показано ниже,Cubic Spline Program

clear all 
%Function to Interpolate 
k = 10;     %Number of Support Nodes-1 
xs(1) = -1; 
for j = 1:k 
    xs(j+1) = -1 +2*j/k; %Support Nodes(Equidistant) 
end; 
fs = 1./(25.*xs.^2+1);  %Support Ordinates 
x = [-0.99:2/(2*k):0.99]; %Places to Evaluate Function 
fx = 1./(25.*x.^2+1);  %Function Evaluated at x 

%Cubic Spline Code(Coefficients to Calculate 2nd Derivatives) 

f(1) = 2*(xs(3)-xs(1)); 
g(1) = xs(3)-xs(2); 
r(1) = (6/(xs(3)-xs(2)))*(fs(3)-fs(2)) + (6/(xs(2)-xs(1)))*(fs(1)-fs(2)); 
e(1) = 0; 

for i = 2:k-2 
    e(i) = xs(i+1)-xs(i); 
    f(i) = 2*(xs(i+2)-xs(i)); 
    g(i) = xs(i+2)-xs(i+1); 
    r(i) = (6/(xs(i+2)-xs(i+1)))*(fs(i+2)-fs(i+1)) + ... 
      (6/(xs(i+1)-xs(i)))*(fs(i)-fs(i+1)); 
end 
e(k-1) = xs(k)-xs(k-1); 
f(k-1) = 2*(xs(k+1)-xs(k-1)); 
r(k-1) = (6/(xs(k+1)-xs(k)))*(fs(k+1)-fs(k)) + ... 
     (6/(xs(k)-xs(k-1)))*(fs(k-1)-fs(k)); 

%Tridiagonal System 

i = 1; 
A = zeros(k-1,k-1); 
while i < size(A)+1; 
    A(i,i) = f(i); 
    if i < size(A); 
     A(i,i+1) = g(i); 
     A(i+1,i) = e(i); 
    end 
    i = i+1; 
end 

for i = 2:k-1        %Decomposition 
    e(i) = e(i)/f(i-1); 
    f(i) = f(i)-e(i)*g(i-1); 
end 

for i = 2:k-1        %Forward Substitution 
    r(i) = r(i)-e(i)*r(i-1); 
end 

xn(k-1)= r(k-1)/f(k-1); 
for i = k-2:-1:1       %Back Substitution 
    xn(i) = (r(i)-g(i)*xn(i+1))/f(i); 
end 

%Interpolation 

if (max(xs) <= max(x)) 
    error('Outside Range'); 
end 

if (min(xs) >= min(x)) 
    error('Outside Range'); 
end 


P = zeros(size(length(x),length(x))); 
i = 1; 
for Counter = 1:length(x) 
    for j = 1:k-1 
     a(j) = x(Counter)- xs(j); 
    end 
    i = find(a == min(a(a>=0))); 
    if i == 1 
     c1 = 0; 
     c2 = xn(1)/6/(xs(2)-xs(1)); 
     c3 = fs(1)/(xs(2)-xs(1)); 
     c4 = fs(2)/(xs(2)-xs(1))-xn(1)*(xs(2)-xs(1))/6; 
     t1 = c1*(xs(2)-x(Counter))^3; 
     t2 = c2*(x(Counter)-xs(1))^3; 
     t3 = c3*(xs(2)-x(Counter)); 
     t4 = c4*(x(Counter)-xs(1)); 
     P(Counter) = t1 +t2 +t3 +t4; 
    else 
     if i < k-1 
     c1 = xn(i-1+1)/6/(xs(i+1)-xs(i-1+1)); 
     c2 = xn(i+1)/6/(xs(i+1)-xs(i-1+1)); 
     c3 = fs(i-1+1)/(xs(i+1)-xs(i-1+1))-xn(i-1+1)*(xs(i+1)-xs(i-1+1))/6; 
     c4 = fs(i+1)/(xs(i+1)-xs(i-1+1))-xn(i+1)*(xs(i+1)-xs(i-1+1))/6; 
     t1 = c1*(xs(i+1)-x(Counter))^3; 
     t2 = c2*(x(Counter)-xs(i-1+1))^3; 
     t3 = c3*(xs(i+1)-x(Counter)); 
     t4 = c4*(x(Counter)-xs(i-1+1)); 
     P(Counter) = t1 +t2 +t3 +t4; 
     else 
     c1 = xn(i-1+1)/6/(xs(i+1)-xs(i-1+1)); 
     c2 = 0; 
     c3 = fs(i-1+1)/(xs(i+1)-xs(i-1+1))-xn(i-1+1)*(xs(i+1)-xs(i-1+1))/6; 
     c4 = fs(i+1)/(xs(i+1)-xs(i-1+1)); 
     t1 = c1*(xs(i+1)-x(Counter))^3; 
     t2 = c2*(x(Counter)-xs(i-1+1))^3; 
     t3 = c3*(xs(i+1)-x(Counter)); 
     t4 = c4*(x(Counter)-xs(i-1+1)); 
     P(Counter) = t1 +t2 +t3 +t4; 
     end  
    end 
end 

P = P'; 
P(length(x)) = NaN; 

plot(x,P,x,fx) 

При запуске кода, функция интерполяции не является симметричным, и она не будет правильно сходятся. Может ли кто-нибудь предложить какие-либо предложения о проблемах в моем коде? Благодарю.

ответ

9

Я написал кубический сплайн-пакет в Mathematica давным-давно. Вот мой перевод этого пакета в Matlab. Примечание. Я не рассматривал кубические сплайны примерно через 7 лет, поэтому я основываю это на своей собственной документации. Вы должны проверить все, что я говорю.

Основная проблема заключается в предоставлении данных n данных (x(1), y(1)) , ... , (x(n), y(n)) и мы хотим рассчитать кусочно-кубическую интерполяцию. Интерполянт определяются как

S(x) = { Sk(x) when x(k) <= x <= x(k+1) 
      { 0  otherwise 

Здесь Sh (х) является кубическим полиномом вида

Sk(x) = sk0 + sk1*(x-x(k)) + sk2*(x-x(k))^2 + sk3*(x-x(k))^3 

Свойства сплайна является:

  1. сплайна проходит через данные точка Sk(x(k)) = y(k)
  2. Сплайн непрерывный в конечных точках и, следовательно, непрерывный всюду в интерполяционном интервале Sk(x(k+1)) = Sk+1(x(k+1))
  3. сплайна имеет непрерывную первую производную Sk'(x(k+1)) = Sk+1'(x(k+1))
  4. сплайна имеет непрерывную вторую производную Sk''(x(k+1)) = Sk+1''(x(k+1))

Для построения кубического сплайна из набора точки данных нам нужно решить для коэффициентов sk0, sk1, sk2 и sk3 для каждого из n-1 кубических полиномов. Это всего 4*(n-1) = 4*n - 4 неизвестных. Свойство 1 поставляет n ограничений и свойств 2,3,4 каждый, снабжает дополнительные n-2 ограничениями. Таким образом, мы имеем n + 3*(n-2) = 4*n - 6 ограничений и 4*n - 4 неизвестных. Это оставляет две степени свободы. Фиксируем эти степени свободы, полагая вторую производную равной нулю на начальном и конечном узлах.

, h(k) = x(k+1) - x(k) и d(k) = (y(k+1) - y(k))/h(k)

. Следующие три перспективе рецидивы имеет место соотношение

h(k-1)*m(k-1) + 2*(h(k-1) + h(k))*m(k) + h(k)*m(k+1) = 6*(d(k) - d(k-1)) 

М (К) неизвестные, которые мы хотим решить для. h(k) и d(k) определяются входными данными. Это трехчленное рекуррентное соотношение определяет трехдиагональную линейную систему. После того, как m(k) определяются коэффициенты для Sk дается

sk0 = y(k) 
    sk1 = d(k) - h(k)*(2*m(k) + m(k-1))/6 
    sk2 = m(k)/2 
    sk3 = m(k+1) - m(k)/(6*h(k)) 

Хорошо, что это вся математика вам нужно знать, чтобы полностью определить алгоритм для вычисления кубического сплайна.Здесь в Matlab:

function [s0,s1,s2,s3]=cubic_spline(x,y) 

if any(size(x) ~= size(y)) || size(x,2) ~= 1 
    error('inputs x and y must be column vectors of equal length'); 
end 

n = length(x) 

h = x(2:n) - x(1:n-1); 
d = (y(2:n) - y(1:n-1))./h; 

lower = h(1:end-1); 
main = 2*(h(1:end-1) + h(2:end)); 
upper = h(2:end); 

T = spdiags([lower main upper], [-1 0 1], n-2, n-2); 
rhs = 6*(d(2:end)-d(1:end-1)); 

m = T\rhs; 

% Use natural boundary conditions where second derivative 
% is zero at the endpoints 

m = [ 0; m; 0]; 

s0 = y; 
s1 = d - h.*(2*m(1:end-1) + m(2:end))/6; 
s2 = m/2; 
s3 =(m(2:end)-m(1:end-1))./(6*h); 

Вот код для построения кубического сплайна:

function plot_cubic_spline(x,s0,s1,s2,s3) 

n = length(x); 

inner_points = 20; 

for i=1:n-1 
    xx = linspace(x(i),x(i+1),inner_points); 
    xi = repmat(x(i),1,inner_points); 
    yy = s0(i) + s1(i)*(xx-xi) + ... 
    s2(i)*(xx-xi).^2 + s3(i)*(xx - xi).^3; 
    plot(xx,yy,'b') 
    plot(x(i),0,'r'); 
end 

Вот функция, которая строит сплайна и участки в на знаменитой функции Рунге:

function cubic_driver(num_points) 

runge = @(x) 1./(1+ 25*x.^2); 

x = linspace(-1,1,num_points); 
y = runge(x); 

[s0,s1,s2,s3] = cubic_spline(x',y'); 

plot_points = 1000; 
xx = linspace(-1,1,plot_points); 
yy = runge(xx); 

plot(xx,yy,'g'); 
hold on; 
plot_cubic_spline(x,s0,s1,s2,s3); 

Вы можете увидеть его в действии, выполнив следующие действия в Matlab подскажет

>> cubic_driver(5) 
>> clf 
>> cubic_driver(10) 
>> clf 
>> cubic_driver(20) 

К тому времени, когда у вас двадцать узлов, ваш интерполятор визуально неотличим от функции Runge.

Некоторые комментарии к коду Matlab: я не использую никаких циклов while или while. Я могу векторизовать все операции. Я быстро формирую разреженную трехдиагональную матрицу с spdiags. Я решаю его с помощью оператора обратной косой черты. Я рассчитываю на UMFPACK Тима Дэвиса, чтобы справиться с декомпозицией, а также вперед и назад.

Надеюсь, что это поможет. Код доступен как на GitHub суть https://gist.github.com/1269709

 Смежные вопросы

  • Нет связанных вопросов^_^