Numerische Methoden

Polynominterpolation

"Man muß es wohl einfach mal ausprobieren", dachte ich mir in der VL Mathe für Informatiker III. Die beiden vorgestellten Methoden zur Bestimmung eines Polynoms, das eine Kurve, die durch endlich viele Stützwerte gegeben ist, annähert, sind wirklich einfach in ein Programm "zu gießen".

Im folgenden habe ich für beide Methoden ein C++ Programm geschrieben, in dem zu erkennen ist, wie die angegebenen Formel 1:1 umgesetzt worden sind.

Außerdem habe ich ein Java-Applet "InterpolationApplet" geschrieben, in dem Referenzkurven und Stützwerte ausgewählt werden können und das resultierende Interpolationspolynom graphisch und als Funktionsterm ausgegeben werden. Mathematik interaktiv, sozusagen. (Endlich mal ein nützliches Applet)

Problemstellung

Es seien endlich viele Punkte in einem Intervall [a,b] gegeben mit a=x0<x1<...<xn=b. Ebenfalls gegeben sind dazugehörige Funktionswerte yi. Gesucht ist ein Polynom p(x) vom Grad kleiner gleich n und p(xi)=yi.

Formel nach Aitken

Hierbei beziehe ich mich auf die Rekursionsformel, die in der VL als erste unter der Teilüberschrift "Praktische Berechnung" angegeben wurde. Danach ist p0,n das gesuchte, eindeutig bestimmte Interpolationspolynom. Die Rekursionsvorschrift ist in Satz 1.2.2 angegeben. Folgendes Programm berechnet pi,l(x) für einzelne x.

Zunächsteinmal benötigen wir sinnvollerweise eine Datenstruktur für einen Punkt im R2:


typedef long double fzahl;



typedef struct s_point {

  fzahl x;

  fzahl y;

} point;

Obwohl eine Rekursionsformel angegeben wurde, wird diese Rekursion beseitigt und die sonst durch Rekursion ermittelten Zwischenwerte werden in einem 2-dimensionalen Array pa gehalten. Außerdem beötigen wir ein Array der Stützwerte, pstellen. Die Dimension hängt von n ab, es gibt n+1 Stützstellen. Als C++-Text:


/** Polynomgrad */

const int n = 3; 



fzahl pa[n+1][n+1]; // pa[i][l] entspricht p_{i,l}

point pstellen[n+1]; // die stützstellen

Bevor die Berchnung beginnen kann, müssen natürlich die Stützwerte bekannt sein, d.h. pstellen muß mit Werten gefüllt werden.

Das Array pa läuft direkt auf ein Dreiecksschema hinaus: als Initalisierung wird pi,0=yi für i=0,...,n gesetzt (z.B. in main()):


for(int i = 0;i<=n;i++) 

  pa[i][0] = pstellen[i].y;

und dann verlangt die Berechnung von pi,l die Werte von pi,l-1 und pi+1,l-1 (siehe Formel!). Mit dieser Information können wir die Funktion schreiben:


fzahl y1(fzahl x)

{

  for(int l=1;l<=n;l++)

    for(int i=0;i<=n-l;i++){

      pa[i][l] = ((x-pstellen[i].x)*pa[i+1][l-1]

		 + (pstellen[i+l].x - x)*pa[i][l-1])/

	(pstellen[i+l].x - pstellen[i].x);

    }

  return pa[0][n];

}

Das war auch schon alles! Natürlich gibts den kompletten Quelltext.

Die Newton-Darstellung

Die Newton-Darstellung ist lediglich eine andere Formel, um auf das selbe Interpolationspolynom zu kommen, wie bei der Aitken-Form.

Hier zunächst die Formel: p(x) = y[x0] + (x-x0)y[x0,x1] +(x-x0)(x-x1)y[x0,x1,x2] + ... + (x-x0)...(x-xn-1)y[x0,...xn] .

Zunächst stellen wir fest, daß sich hier die Faktoren (x-xi) n-imal wiederholen. Für ein festes x muß dies also nur einmal berechnet werden. y[] bezeichnet man als dividierten Differenzen. Sie ergeben sich durch Rekursion, die sich wieder (ähnlich wie bei der Aitkenform) in einer Dreiecksform darstellen lassen. Hier die Rekursionsformeln:

y[xi] = yi.

y[x0,...,xi+l] = (1/(xi+l-xi)) *(y[xi+1,...,xi+l]-y[xi+1,...,xi+l])

Im Programm brauchen wir dafür wieder ein zweidimensionales Feld der Dimension n+1, daß entsprechend der ersten Zeile initalisiert werden muß:


fzahl divdiff[n+1][n+1]; 



int main()

{ 

  // ...

  for(int i = 0;i<=n;i++) 

    divdiff[i][0] = pstellen[i].y;

  // ...

}

Dabei entspricht divdiff[i][j] dem Term y[xi,...,xi+j].

In der Funktion wird noch ein Feld der Länge n angelegt, in dem die Werte der Faktoren der Form (x-xi) gespeichert werden. Dann erfolgt die Berechnung. Genau wie bei Aitken gibt es zwei verschachtelte Schleifen, in der Inneren werde die dividierten Differenzen berechnet, in der Äußeren dann der entsprechende Summand des Interpolationpolynoms:


fzahl yNewton(fzahl x)

{

  /** xdiff[i] = produkt [j=0 bis i-1] (x-x_j) */

  fzahl xdiff[n+1]; 

  // ergebnis

  fzahl erg = 0.0;

  

  xdiff[0] = 1.0;

  erg = divdiff[0][0]; // = y_0



  for(int l=1;l<=n;l++){

    for(int i=0;i<=n-l;i++){

      divdiff[i][l] = (divdiff[i+1][l-1]-divdiff[i][l-1])/

	(pstellen[i+l].x-pstellen[i].x);

    }

    xdiff[l] = xdiff[l-1] * (x-pstellen[l-1].x);

    erg += xdiff[l]*divdiff[0][l];

  }

  return erg;

  

}

Wie hierbei ersichtlich ist, sind die dividierten Differenzen gar nicht vom aktuellen Wert von x abhängig, sondern nur von den Stützwerten. Wenn also ein Interpolationspolynom an mehreren Stellen bei den gleichen Stützwerten berechnet wird, reicht die einmalige Berechnung der dividierenden Differenzen. Dies wurde in der Java-Version implementiert.

Die Newton-Darstellung (und auch die Aitken-Form) ist gegenüber der Reihenfolge der Stützwerte invariant, soll heißen, es muß nicht unbedingt x0<...<xn gelten; die Stützwerte können vielmehr in beliebiger Reihenfolge vorliegen.

Dies ermöglicht es, bei einer Hinzunahme weiterer Stützwerte einfach nur den notwendigen Teil der dividierten Differenzen neu zu berechnen, da nicht alle Werte von den neuen abhängen.

Die Ausgabe des Interpolationspolynoms als Zeichenkette habe ich nur in Java implementiert, da hier gegenüber C++ eine wesentlich einfachtes Stringhandling vorliegt. Es unterscheidet sich jedoch (logischerweise) nicht grundsätzlich von der Berechnung des Interpolationspolynoms.

 


(P)+(C) by Erik Pischel
Last modified: Mon Nov 23 21:13:22 MET