Universität Hamburg - Fachbereiche - Fachbereich Mathematik

Java-Kurs (4)

Numerische Mathematik, WiSe 99/00 und SoSe 00, Bodo Werner

Zurück zum Inhaltsverzeichnis.

Splineinterpolation

Projektaufgabe 1

Erweiterung der Klassen Interpolation und NewtonInterpolation, neue Klassen LinearerSpline und LineareSplineInterpolation
Hier soll der Fall, dass eine Funktion f interpoliert werden soll und dass somit die InterpolationsFunktion eine "Ersatzfunktion" für f wird, erfasst werden. Bei der Polynominterpolation kommen hierdurch die T-Knoten ins Spiel. Ferner gibt es einen Sinn, von einer Interpolationsfehlerfunktion zu reden.

Dazu wird die abstrakte Klasse Interpolation um ein Attribut in Form eines Objekts namens zuInterpolierendeFunktion der Klasse Funktion erweitert. Ein zweiter Konstruktor (sowohl in der Klasse Interpolation als auch in deren Unterklasse NewtonInterpolation) muss her, der dieses Objekt übergibt und zu gegebenen Knoten die (Interpolations-)Werte durch Funktionsauswertung berechnet.

Für die grafische Darstellung einer solchen Interpolation bieten sich zwei Grafikfenster an: in dem ersten werden die beiden Graphen der Funktionen zuInterpolierendeFunktion und InterpolationsFunktion sowie die Interpolationspunkte, in einem zweiten Fenster der Graph der Interpolationsfehlerfunktion sowie die Knoten auf der x-Achse gezeigt. Zu diesem Zweck soll eine neue (öffentliche) Methode Zeichne() der Klasse Interpolation hinzugefügt werden. Um aber auch den einfacheren Fall zu erfassen, dass nur Interpolationspunkte gegeben sind, zu denen eine Interpolationsfunktion berechnet werden soll, kann man eine Variable vom Typ boolean als Attribut in der Klasse Interpolation einführen, etwa mit dem Bezeichner FunktionsInterpolation.

Der Vorteil des OOP macht sich hier ganz deutlich bezahlt, wenn man eine andere Interpolation als die Polynominterpolation verwendet. Dies ist das Ziel des Teils 2 der Projektaufgabe 1: An die Stelle der Klasse NewtonInterpolation tritt die Klasse LineareSplineInterpolation, die das Objekt namens InterpolationsFunktion in ihrem Konstruktor völlig analog zu dem von NewtonInterpolation berechnet, nur, dass an die Stelle der Zeilen

double[] c = DividierteDifferenzen();
InterpolationsFunktion = new NewtonPolynom(x,c);
die einfachere Zeile
InterpolationsFunktion= new LinearerSpline(x,y);
tritt, wobei die Klasse LinearerSpline eine Unterklasse von Funktion ist, deren Methode getY() natürlich vernünftig implementiert werden muss.

Sind x[] gegebene Knoten und f eine zu interpolierende Funktion, so kann man in einer Applikationsklasse durch

 
NewtonInterpolation NIP = new NewtonInterpolation(x,f); 
NIP.Zeichne(); 
die Polynominterpolation und durch
 
LineareSplineInterpolation SIP = new LineareSplineInterpolation(x,f); 
SIP.Zeichne(); 
die lineare Splineinterpolation visualisieren. Ist das nicht eine großartige Anwendung des Vererbungsprinzips des OOP? Später kann auch eine kubischeSplineInterpolation hinzukommen.

Kubische Splines

Wir interessieren uns hier nur für kubische Splines S, die mindestens 1-glatt, also 1x stetig-differenzierbar sind. In der von mir bevorzugten Version sind diese eindeutig gegeben, wenn die Knoten ( double[] t), die Werte (double[] f)) und die "Steigungen" (double[] s, Ableitungen von S an den Knoten) gegeben. Dann ist es nur noch ein kleiner Schritt zur
public class kubischerSpline extends Funktion{
    private double[] t, f, s; //Knoten, Werte, Ableitungen
    public kubischerSpline(double[] x, double[] y, double[] s){
    super(x[0],x[x.length-1]);
	t=x; f=y; this.s=s;
    }
    public double getY(double x){
	/*1.Bestimme j mit t[j]<=x<=t[j+1]
         2.Werte das durch t[j],f[j],s[j],t[j+1],f[j+1],s[j+1] gegebene
           kubische Hermite-Interpolationpolynom aus.*/
    }//Ende getY
}//Ende class kubischerSpline
Will man eine Interpolationsaufgabe zu den Interpolationspunkten (t[],f[]) lösen, so wissen wir von der Theorie, dass dies durch einen 2-glatten kubischen Spline gelingt, wobei sogar noch zwei Bedingungen frei sind. In jedem Fall muss ein lineares Gleichungssystem mit den Unbekanntes s[] gelöst werden. Wie dies geht, wird bis zum Kapitel "Lineare Gleichungssysteme" aufgeschoben. Klar ist nur schon jetzt, wie vorgegnagen werden muss:
public class kubischeSplineInterpolation extends Interpolation{
/* Der Konstruktor %uuml;bergibt die Interpolationspunkte 
double[] x, double[] y (Daten der Klasse
Interpolation), die Festlegung der zwei offenen
Bedingungen sowie evtl. eine
zuInterpolierendeFunktion. Gegebenenfalls sind mehrere
verschiedene Konstruktoren vorzusehen. Innerhalb der Konstruktoren
wird mit Hilfe einer späteren Klasse Matrix das
zugehörige lineare Gleichungssystem für die s[]
gelöst und mittels */
InterpolationsFunktion k =new kubischerSpline(t,f,s);
//berechnet.
Danach ist es ein leichtes, die Klasse NumAnalysis um einige Methoden namens SplineInterpolation() zu bereichern.

Java-Fallen

Referenzvariable, NullPointer
Hierzu siehe auch ein ausführlicheres ps-File, das man hier herunterladen kann.

Ein häufiger verhängsnisvoller Fehler tritt bei Objekten derselben Klasse mit unterschiedlichen Namen - sagen wir A1 und A2 - auf, wenn man A1 schon allokiert hat und mittels A2=A1 eine Kopie anlegen will. Wenn danach A2 verändert wird, so wird A1 in einem Zug mitverändert, meist ungewollt. Dies liegt daran, dass A1 und A2 Zeiger- oder Referenzvariable sind, die ab dem Befehl A2=A1 auf denselben Speicherbereich zeigen. Alle Änderungen von A1 oder A2 finden in diesem Speicherbereich statt. BeispieL:

double[] x={1,2,3};
double[] y;
y=x; 
y[1]=x[1]/2; x[1]*=3;
//jetzt gilt x[1]=3=y[1]!!  s. Klausuraufgabe
Will man wirklich eine Kopie anlegen, so kommt die Methode clone() der obersten Klasse object zum Zuge: A2=(Klassennamen) A1.clone(), oder an unserem Beispiel: y=(double[]) x.clone();.

Eine am Anfang mysteriöse Laufzeit-Fehlermeldung lautet NullPointerException. Diese Meldung tritt immer dann auf, wenn Objekte nur deklariert, aber nicht allokiert sind (ihre Variablen sind dann NullPointer, sie zeigen auf Null), man jedoch auf ihre Komponenten zugreifen will. Z.B. bei einem Feld, das durch double[] x; deklariert wurde und auf das später etwa durch ... x[2] ... zugegriffen wird.

Ein weiterer häfiger Fehler entsteht bei der Division von int-Zahlen:

 int i=3; int j=5;
 double x=i/j; //liefert  x=0
 double y=j/i; //liefert  y=1 
//richtig:
double x= (double) i/j; //Casting

Weiter mit "Numerische Integration"