Interpolácia pozostáva z odhadu hodnot (bodov) funkcie medzi zadanými hodnotami (x0, y0), (x1, y1), ... (xi-1, yi-1), (xi, yi), (xi+1, yi+1), ... (xn, yn). Hodnoty sa interpolujú vždy medzi dvojicou zadaných hôdnot, čize pre nejaký interval. Pričom do výpočtu hodnôt z daného intervalu vtupujú hodboty z predchádzajúceho aj nasledujúcého intervalu, tým sa dosiahne hladši a presnejši priebeh interpolovanej funkcie. Podrobné vysvetlenie a odvodzovanie výrazov pre spline interpoláciu je voľne dostupné na webe. Takže len pre informáciu sú nižsie uvedené už len odvodené vzorce, ktoré sa využivajú v programe. Cieľom je vypočitať koeficienty "a, b, c, d" pre jednotlivé intervaly a pomocou polynómu tretieho rádu získať interpolované body funkcie a teda priebeh interpolovanej funkcie.
Polynóm tretieho rádu pre výpočet bodov funkcie:
Na výpočet jednotlivých koeficientov je potrebné poznat prvú a druhú deriváciu:
Po úprave (úprava je nutná aby v zdrojovom kóde nenastalo delenie nulov, v prípade ak je y súradnica funkcie rovnaká pre rôzne x):
Pričom pre krajné (hraničné) body platí:
Druhé derivácie:
Koeficienty pre jednotlive intevaly zadaných hodnôt sa vypočítajú podľa vzorcov:
Ako príklad je uvedená interpolácia pre 6 intervalov, tzn. pre 7 zadaných súradníc (x,y):
Graf interpolácie - zadané body (červená farba), vypočítané body (modrá farba):
Zdrojový kód
public bool splineInterpolation(out string errorMessage, double[] xInArray, double[] yInArray, out double[] yOutArray, out double[,] koeffArray) { errorMessage = ""; if (xInArray.Length < 3) { errorMessage = "Array Lenght Error: Vstupne pole (polia) ma nespravnu dlzku."; yOutArray = new double[0]; koeffArray = new double[0, 0]; return false; } int pointCount = Convert.ToInt32(xInArray[xInArray.Length - 1]) - Convert.ToInt32(xInArray[0]) + 1; yOutArray = new double[pointCount]; double f1x0_deriv = 0; double f1x1_deriv = 0; double f2x0_deriv = 0; double f2x1_deriv = 0; int intervalLenght = xInArray.Length - 1; koeffArray = new double[intervalLenght, 4]; try { if (xInArray.Length != yInArray.Length) { errorMessage = "Array Lenght Error: Vstupne pole (polia) ma nespravnu dlzku."; return false; } //Vypocet koeficientov pre jednotlive intervaly for (int iInterval = 0; iInterval < intervalLenght; iInterval++) { if (iInterval == 0) { f1x1_deriv = (2 * (yInArray[iInterval + 2] - yInArray[iInterval + 1]) * (yInArray[iInterval + 1] - yInArray[iInterval])) / ((xInArray[iInterval + 2] - xInArray[iInterval + 1]) * (yInArray[iInterval + 1] - yInArray[iInterval]) + (xInArray[iInterval + 1] - xInArray[iInterval]) * (yInArray[iInterval + 2] - yInArray[iInterval + 1])); f1x0_deriv = ((3 * (yInArray[iInterval + 1] - yInArray[iInterval])) / (2 * (xInArray[iInterval + 1] - xInArray[iInterval]))) - (f1x1_deriv / 2); f2x0_deriv = ((-2 * (f1x1_deriv + 2 * f1x0_deriv)) / (xInArray[iInterval + 1] - xInArray[iInterval])) + ((6 * (yInArray[iInterval + 1] - yInArray[iInterval])) / Math.Pow(xInArray[iInterval + 1] - xInArray[iInterval], 2)); f2x1_deriv = ((2 * (2 * f1x1_deriv + f1x0_deriv)) / (xInArray[iInterval + 1] - xInArray[iInterval])) - ((6 * (yInArray[iInterval + 1] - yInArray[iInterval])) / Math.Pow(xInArray[iInterval + 1] - xInArray[iInterval], 2)); } else { if (iInterval == intervalLenght - 1) { f1x0_deriv = (2 * (yInArray[iInterval + 1] - yInArray[iInterval]) * (yInArray[iInterval] - yInArray[iInterval - 1])) / ((xInArray[iInterval + 1] - xInArray[iInterval]) * (yInArray[iInterval] - yInArray[iInterval - 1]) + (xInArray[iInterval] - xInArray[iInterval - 1]) * (yInArray[iInterval + 1] - yInArray[iInterval])); f1x1_deriv = ((3 * (yInArray[iInterval + 1] - yInArray[iInterval])) / (2 * (xInArray[iInterval + 1] - xInArray[iInterval]))) - (f1x0_deriv / 2); f2x0_deriv = ((-2 * (f1x1_deriv + 2 * f1x0_deriv)) / (xInArray[iInterval + 1] - xInArray[iInterval])) + ((6 * (yInArray[iInterval + 1] - yInArray[iInterval])) / Math.Pow(xInArray[iInterval + 1] - xInArray[iInterval], 2)); f2x1_deriv = ((2 * (2 * f1x1_deriv + f1x0_deriv)) / (xInArray[iInterval + 1] - xInArray[iInterval])) - ((6 * (yInArray[iInterval + 1] - yInArray[iInterval])) / Math.Pow(xInArray[iInterval + 1] - xInArray[iInterval], 2)); } else { f1x1_deriv = (2 * (yInArray[iInterval + 2] - yInArray[iInterval + 1]) * (yInArray[iInterval + 1] - yInArray[iInterval])) / ((xInArray[iInterval + 2] - xInArray[iInterval + 1]) * (yInArray[iInterval + 1] - yInArray[iInterval]) + (xInArray[iInterval + 1] - xInArray[iInterval]) * (yInArray[iInterval + 2] - yInArray[iInterval + 1])); f1x0_deriv = (2 * (yInArray[iInterval + 1] - yInArray[iInterval]) * (yInArray[iInterval] - yInArray[iInterval - 1])) / ((xInArray[iInterval + 1] - xInArray[iInterval]) * (yInArray[iInterval] - yInArray[iInterval - 1]) + (xInArray[iInterval] - xInArray[iInterval - 1]) * (yInArray[iInterval + 1] - yInArray[iInterval])); f2x0_deriv = ((-2 * (f1x1_deriv + 2 * f1x0_deriv)) / (xInArray[iInterval + 1] - xInArray[iInterval])) + ((6 * (yInArray[iInterval + 1] - yInArray[iInterval])) / Math.Pow(xInArray[iInterval + 1] - xInArray[iInterval], 2)); f2x1_deriv = ((2 * (2 * f1x1_deriv + f1x0_deriv)) / (xInArray[iInterval + 1] - xInArray[iInterval])) - ((6 * (yInArray[iInterval + 1] - yInArray[iInterval])) / Math.Pow(xInArray[iInterval + 1] - xInArray[iInterval], 2)); } } //koeficienty a, b, c, d - stlpce, intervaly - riadky koeffArray[iInterval, 0] = (f2x1_deriv - f2x0_deriv) / (6 * (xInArray[iInterval + 1] - xInArray[iInterval])); koeffArray[iInterval, 1] = (xInArray[iInterval + 1] * f2x0_deriv - xInArray[iInterval] * f2x1_deriv) / (2 * (xInArray[iInterval + 1] - xInArray[iInterval])); koeffArray[iInterval, 2] = ((yInArray[iInterval + 1] - yInArray[iInterval]) - koeffArray[iInterval, 1] * (Math.Pow(xInArray[iInterval + 1], 2) - Math.Pow(xInArray[iInterval], 2)) - koeffArray[iInterval, 0] * (Math.Pow(xInArray[iInterval + 1], 3) - Math.Pow(xInArray[iInterval], 3))) / (xInArray[iInterval + 1] - xInArray[iInterval]); koeffArray[iInterval, 3] = yInArray[iInterval] - koeffArray[iInterval, 2] * xInArray[iInterval] - koeffArray[iInterval, 1] * Math.Pow(xInArray[iInterval], 2) - koeffArray[iInterval, 0] * Math.Pow(xInArray[iInterval], 3); } int start = Convert.ToInt32(xInArray[0]); int intervalNum = 0; for (int i = start; i < pointCount + start; i++) { while ((Convert.ToDouble(i) >= xInArray[intervalNum] && Convert.ToDouble(i) <= xInArray[intervalNum + 1]) != true) { intervalNum++; } yOutArray[i - start] = koeffArray[intervalNum, 3] + koeffArray[intervalNum, 2] * i + koeffArray[intervalNum, 1] * Math.Pow(i, 2) + koeffArray[intervalNum, 0] * Math.Pow(i, 3); } } catch (Exception ex) { errorMessage = ex.Message; return false; } return true; }