Matrix
(Datei: Matrix.java) sowie ihrer Unterklasse
TridiagonalMatrix
(Datei: TridiagonalMatrix.java) orientieren.
Diese Klassen können von irgendeiner Applikation (eine
Klasse mit Methode
main
) benutzt werden. Hier wird dies die Klasse
HauptMatrix
sein.
main
).
Der Programmierer wird daher, allerdings nur in sehr schwacher
Weise (s. die Beispiele
Lösung eines linearen Gleichungssystems
und
Berechnung eines Newtonschen Interpolationspolynoms)
zur OOP gezwungen. Schaut man sich diese Beispiele
oder die allereinfachste Anwendung in Form des
Hello-World-Programms an, kann man keinen Nutzen von OOP
erkennen, eher nur Mystik. Das liegt u.a. daran, daß die
hier definierten Klassen in dem Sinne abstrakt sind, als
daß kein
Exemplar (auch Instanz genannt)
von ihr gebildet wird.
int
oder double
sind keine eigentlichen Objekte, alle Variablen von einem anderen als einem
Grunddatentyp sind stets Objekte.
Beispiel:
Mittels Matrix A;
wird ein Objekt mit Bezeichner
A
der Klasse Matrix
deklariert.
Unter Verwendung des
new
-Operators
und einem Konstruktor
gleichen Namens wie die Klasse wird das Objekt sodann
allokiert, d.h.
ihm Speicherplatz (dynamisch) reserviert:
Durch
double[] a={{1,2},{3,4}};
A=new Matrix(2,2,a)
wird eine 2x2-Matrix mit den durch a
gegebenen Elementen
initialisiert.
Auf die Daten (Mitgliedsvariable) und Methoden eines Objekts wird mit der Punkt-Notation zugegriffen.
( Listing der Klassen Matrix, TridiagonalMatrix )
static
kenntlich gemacht werden.
Beispiel:
Ein Objekt namens A
der Klasse Matrix
hat als Mitgliedsvariable (u.a.) die Abmessungen
int m, n
und die Elemente double[][] Data
.
Mittels
A.m, A.Data
könnte von außen auf die
Daten von A
zugegriffen werden, wenn diese nicht
als
private
deklariert worden wären (s.u. unter
Zugriffsrechte).
( Listing der Klassen Matrix, TridiagonalMatrix )
void
festgelegt
wird). Methoden können in runden Klammern eingeschlossene
Parameter (Argumente), auch in Form von
Parameterlisten, enthalten. Die runden Klammern müssen
immer dem Bezeichner einer Methode angefügt werden - auch wenn es keinen
Parameter gibt.
Matrix
(und auch
ihr Objekt A
) u.a. über
von denen nurprivate void LRZerlegung() public double Spur() public double Determinante() public double[] LoeseLGS(double[] b)
LRZerlegung()
keinen Wert
(void
)
zurückgibt, während die anderen Methoden
als Wert double
oder double[]
haben.
Ferner hat nur LoeseLGS
einen
Parameter in Gestalt eines Feldes double[] b
, der
für eine rechte Seite eines LGS steht. Zu den
Zugriffsrechten später.
x=A.LoeseLGS(b);
kann die Methode
LoeseLGS
(nach Deklaration der Felder x,b
durchgeführt werden.
Man sagt auch, daß eine
Klasse, in der A
deklariert wird,
sich der (öffentlichen!)
Methode LoeseLGS()
des Objektes A
bedient.
Die Werte von Methoden werden mit Hilfe desclass Matrix{ ... ... public double[] LoeseLGS(double[] b){ int i,j; double[] x; if (LR[0][0]==0) LRZerlegung(); //int n, double[][] LR, int[] z sind "globale" Variable der Klasse Matrix //beginn vorwaertseinsetzen for(i=1;i<n;i++) for(j=0;j<i;j++) b[z[i]]-=LR[z[i]][j]*b[z[j]]; //ende Vorwaertseinsetzen x=new double[n]; //beginn rueckwaertseinsetzen for(i=n-1;i>=0;i--){ for(j=n-1;j>i;j--) b[z[i]]-=LR[z[i]][j]*b[z[j]]; b[z[i]]/=LR[z[i]][i]; }//ende Rueckwaertseinsetzen for(i=0;i<n;i++) x[i]=b[z[i]]; return x; }//ende LoeseLGS public double Determinante(){ double s=0; double sig=1; if (m==n){ if (LR[0][0]==0) LRZerlegung(); s=1; for(int i=0;i<n;i++){ if (z[i]!=i) sig=-sig; s*=sig*LR[this.z[i]][i]; } //for i }// if else System.out.println("Die Matrix ist keine quadratische Matrix"); return s; } //ende Determinante ... ... }//Ende class Matrix
return
Operators übergeben.
( Listing der Klassen Matrix, TridiagonalMatrix )
new
.
Konstruktoren sind ganz spezielle Methoden, sie haben
denselben Namen wie die Klasse.
Eine Klasse muß nicht zwingend
einen Konstruktor vorsehen. Dann kann man immer noch mit einem
voreingestellten "minimalen" Default-Konstruktor ein Objekt allokieren.
Der Konstruktor liefert keine Werte, dennoch steht er ohne den Zusatz
void.
Matrix
kann folgendermaßen vereinbart
werden:
Die Matrix wird "konstruiert", wenn ihre Abmessungen und ihre Elemente übergeben werden. Hier ist die Verwendung des Zeigersclass Matrix{ ... ... //Konstruktor: public Matrix(int m, int n, double[][] Data){ this.m=m; this.n=n; this.Data=Data; } ... ... }//Ende class Matrix
this
nur dann nicht notwendig, wenn man als Parameter-Bezeichner des
Konstruktors nicht dieselben wie die für die Mitgliedsvariablen
verwendet hätte, z.B.
Die Verwendung von//Konstruktor: public Matrix(int nZeile, int nSpalte, double[][] a){ m=nZeile; n=nSpalte; Data=a; }
this
hat den Vorteil, daß man sich
in das Gedächtnis zurückruft, daß m, n, Data
Mitgliedsvariable eines Matrix
-Objektes sind.
( Listing der Klassen Matrix, TridiagonalMatrix )
public Matrix(int m, int n, double[][] Data)
kann (für quadratische Matrizen) durch
überladen werden, indem der erste Konstruktor mit Hilfe des Zeigerspublic Matrix(int n, double[][] Data){ this(n,n,Data); }
this
aufgerufen wird (Konstruktoren können sich gegenseitig mit Hilfe
von this
aufrufen). Danach können beide
Konstruktoren Verwendung finden.
Matrix
werden mehrere Konstruktoren
definiert, die z.T. umfangreicher sind als die hier angegebenen
Möglichkeiten.
( Listing der Klassen Matrix, TridiagonalMatrix )
public
(öffentliches Zugriffsrecht, Sichtbarkeit)
oder private
lautet.
Ersteres erlaubt jeder Klasse,
bzw. jedem Objekt
auf sie zuzugreifen (und die öffentlichen Daten zu verändern),
letzteres gewährt dieses Recht nur innerhalb der eigenen Klasse.
Für Methoden wird hierdurch die Dienstbarkeit für andere Klassen
festgelegt, in der Regel bieten Objekte ihre Dienste anderen Objekten
an.
protected
erlaubt das Zugreifen auch für
Ober- und Unterklassen (s.u.), also für das gesamte
Paket (package
).
public
.
private int m, n;
Hätten wir die Variablen m, n
der Klasse
Matrix
nicht als private
deklariert, könnte eine andere Klasse
z.B. durch A.n=23;
eine "falsche" Spaltenzahl erzeugen.
Will man jedoch Unterklassen (s.u.) definieren, k"onnen diese
private
Attribute und Methoden nicht erben. Diese müssen wenigstens
protected sein. Das wird für unser Beispiel
wichtig, wenn wir eine Unterklasse
TridiagonalMatrix einführen.
Grundsätzlich sind private Daten und Methoden für andere Klassen
nicht sichtbar. Die Methode LRZerlegung()
wird
nur intern verwendet und daher als
private
deklariert.
Will man die LR-Zerlegung ermitteln, kann die bisher nicht besprochene
Methode public void getLR(double[][] LR)
verwendet werden.
( Listing der Klassen Matrix, TridiagonalMatrix )
Math
. Hier sind naturgemäß
alle Variablen und Methoden statisch (static
). Man ruft
sie über den Bezeichner der Klasse auf, z.B.
Math.sin
.
Tools
.
Hier sind naturgemäß
alle Variablen und Methoden statisch (static
).
Matrix A;
ergibt eine (Referenz-) Variable mit
Bezeichner A
vom Typ Matrix
.
Erst durch Initialisierung in Verbindung mit new
wird ein konkretes
Objekt erzeugt. Man sagt, es wird eine Instanz der Klasse als Vorlage
gebildet: Durch
wird im Heap Platz gemacht für die Daten vonA=new Matrix(3,a);
A
, man sagt A ist initialisiert oder
allokiert.
A
ist wie jede Objektvariable einschließlich Arrays und Strings
eine Referenzvariable im Gegensatz zu
int i; char b; double x;
, den Grunddatentypen.
Dieser Unterschied kommt bei der Anweisung B=A;
für zwei
Objekte A, B zum Tragen: hierdurch werden die Referenzen
gleich, d.h. in Folge weisen beide auf denselben Heap, es wird keine
Kopie angelegt!
Das hat Folgen für meine LRZerlegung()
,
in der versucht wird, die Elemente von A nicht zu überschreiben.
Durch LR=Data
würde genau das geschehen, nicht aber durch
Ebenfalls sehr wichtig ist dies, wenn man Referenzvariable als Parameter von Methoden übergibt ("Call by Reference").for(i=0;i<m;i++) for(j=0;j<n;j++) LR[i][j]=Data[i][j];
( Listing der Klassen Matrix, TridiagonalMatrix )
TridiagonalMatrix
ist eine Unterklasse der
Klasse Matrix
.
Dieses erkennt man an der ersten Zeile
in TridiagonalMatrix.java
:
Mit dem Zusatzclass TridiagonalMatrix extends Matrix{ ... ... }
extends Matrix
definiert sich die Klasse
TridiagonalMatrix
als Unterklasse der Klasse
Matrix
.
Der Vorteil dieses Konzepts wird durch Vererbung
und Polymorphie charakterisiert.
super
einen Konstruktor der Superklasse Matrix
aufruft und sodann
ergänzt wird, man spricht von Überlagerung
(oder auch Redefinition) des Konstruktors
der Superklasse:
Nun stehen einem Objekt der Klasseclass Tridiagonal extends Matrix{ private double[] a,b,c,r,l; public TridiagonalMatrix(int n){ //Konstruktor super(n); //Konstruktor der Superklasse, der eine quadratische Matrix erzeugt, //deren elemente erst spaeter festgelegt werden. a=new double[n]; //Diagonale b=new double[n-1];//Superdiagonale c=new double[n];//Subdiagonale r=new double[n];//Diagonale von R l=new double[n];//Subdiagonale von L }//Ende Konstruktor ... ... }//Ende class TridiagonalMatrix
TridiagonalMatrix
alle Daten (Attribute) wie double[][] Data, LR;
zur Verfügung.
( Listing der Klassen Matrix, TridiagonalMatrix )
LRZerlegung()
und
LoeseLGS()
der
Superklasse Matrix
geerbt. Diese werden aber
sinnvollerweise überlagert (redefiniert), damit sie
die Tridiagonalgestalt voll ausnutzen können (wenn kein Zeilentausch
vorgenommen wird). Die LR-Zerlegung wird hier auf zweierlei Weise
abgelegt: zum einen durch die beiden neuen
Daten double[] l, r
,
aber auch durch die geerbten
Daten double[][] LR
der Superklasse:
Die Methodeclass TridiagonalMatrix extends Matrix{ ... ... public void LRZerlegung(){ r[0]=a[0];LR[0][0]=r[0]; for (int i=1;i<n;i++){ l[i]=c[i]/r[i-1]; LR[i][i-1]=l[i]; r[i]=a[i]-l[i]*b[i-1]; LR[i][i]=r[i]; LR[i-1][i]=b[i-1]; }//for }//Ende LRZerlegung() ... ... }//Ende class TridiagonalMatrix
LRZerlegung()
der Superklasse wird also
vollständig neu geschrieben. Analog geschieht dies mit der Methode
LoeseLGS()
:
class TridiagonalMatrix extends Matrix{ ... ... public double[] LoeseLGS(double[] d){ //d: Rechte Seite int i; double[] x; if (LR[0][0]==0) LRZerlegung(); for(i=1;i<n;i++) d[i]-=l[i]*d[i-1]; x=new double[n]; x[n-1]=d[n-1]/r[n-1]; for(i=n-2;i>=0;i--) x[i]=(d[i]-b[i]*x[i+1])/r[i]; return x; }//Ende LoeseLGS ... ... }//Ende class
TridiagonalMatrix
,
z.B. namens T
, so kann man durch den Aufruf
T.LoeseLGS(b);
die im Vergleich zu der gleichlautenden
Methode der Superklasse wesentlich effizientere Methode der
Unterklasse verwenden.
Spur()
und Determinante
brauchen aber nicht modifiziert zu werden. Sie können aber
unbedenklich z.B. mittels spur=T.Spur();
oder
det=T.Determinante();
eingesetzt werden
(Hier zahlt es sich aus, daß die geerbten Daten Data, LR
richtig besetzt wurden).
( Listing der Klassen Matrix, TridiagonalMatrix )
Matrix
enthält, könnte dort
unbedenklich (jedenfalls syntaktisch) ein Objekt der
Unterklasse TridiagonalMatrix
eingesetzt werden.
Dies könnte sich z.B. auf Matrizenmultiplikation oder
auf die Ausgabe einer Matrix beziehen.
class Matrix{ //Daten--------------------------------------- protected int m,n; // Abmessungen einer Matrix protected double[][] Data, LR; //Data: Elemente der Matrix. // LR: nach der LR-Zerlegung private int[] z; //Zeilentausch-Permutationsvektor private char pivotc; //pivotc='z': Zeilentausch, //legt die Pivotstrategie fest public Matrix(int m, int n){ //Konstruktor einer rechteckigen Matrix this.m=m; this.n=n; Data=new double[n][n]; LR=new double[n][n]; z=new int[n]; for(int i=0;i<m;i++) z[i]=i; pivotc=' '; }// Ende 1.Konstruktor public Matrix(int n){ //Konstruktor einer quadratischen Matrix this(n,n); }// Ende 2.Konstruktor public Matrix(int m, int n, double[][] a){ this(m,n); SetDaten(a); }// Ende 3.Konstruktor public Matrix(int n, double[][] a){ this(n,n,a); }//Ende 4.Konstruktor // METHODEN (Konvention: Beginn der Bezeichner mit Gross-Buchstaben) //Im folgenden sind alle "this" nicht notwendig. //Festlegen der Matrixelemente public void SetDaten(double[][] a){ for(int i=0;i<m;i++) for(int j=0;j<n;j++) this.Data[i][j]=a[i][j]; }//Ende SetDaten //Festlegen der Pivotisierungsstrategie: public void SetPivot(char c){ this.pivotc=c; }//Ende SetPivot //Lesen der Matrixelemente: public void GetDaten(double[][] a){ for(int i=0;i<m;i++) for(int j=0;j&th;n;j++) a[i][j]=this.Data[i][j]; }//Ende GetDaten public void GetLR(double[][] a){ if (LR[0][0]==0) LRZerlegung(); for(int i=0;i<m;i++) for(int j=0;j<n;j++) a[i][j]=this.LR[i][j]; }//Ende 1.GetLR public void GetLR(double[][] a, int[] p){ //Ueberlagerung der oberen Methode if (LR[0][0]==0) LRZerlegung(); for(int i=0;i<m;i++){ for(int j=0;j<n;j++) a[i][j]=this.LR[i][j]; p[i]=this.z[i]; }// for i }//Ende 2.GetLR // LR Zerlegung evtl. mit mit Pivotsuche (Zeilentausch): private void LRZerlegung(){ int i,j,k,l,h; double pivot,pivot1; for(i=0;i<m;i++) for(j=0;j<n;j++) LR[i][j]=Data[i][j]; //LR=Data bewirkt, dass in Folge LR=Data,d.h. Data veraendert wird. //Der Grund: LR und Data sind Referenzvariable for(k=0;k<m-1;k++){ if (this.pivotc=='z'){ pivot=0; l=0; for(i=k;i<m;i++){ pivot1= Math.abs(LR[z[i]][k]); if (pivot1>pivot){ pivot=pivot1;l=i; } //if }//Ende for (Pivotsuche) h=z[k];z[k]=z[l];z[l]=h; }//ende if Zeilenpivotierung for(i=k+1;i<m;i++){ LR[z[i]][k]/=LR[z[k]][k]; for (j=k+1;j<n;j++) LR[z[i]][j]-=LR[z[i]][k]*LR[z[k]][j]; }//for i }//for k }// Ende LRZerlegung //LGS loesen: public double[] LoeseLGS(double[] b){ int i,j; double[] x; if (LR[0][0]==0) LRZerlegung(); //beginn vorwaertseinsetzen for(i=1;i<n;i++) for(j=0;j<i;j++) b[z[i]]-=LR[z[i]][j]*b[z[j]]; //ende Vorwaertseinsetzen x=new double[n]; //beginn rueckwaertseinsetzen for(i=n-1;i>=0;i--){ for(j=n-1;j>i;j--) b[z[i]]-=LR[z[i]][j]*b[z[j]]; b[z[i]]/=LR[z[i]][i]; }//ende Rueckwaertseinsetzen for(i=0;i<n;i++) x[i]=b[z[i]]; return x; }//ende LoeseLGS // Spur: public double Spur(){ int i; double s=0; for(i=0;i<n;i++) s+=Data[i][i]; return s; } //ende Spur // Determinante: public double Determinante(){ double s=0; double sig=1; if (m==n){ if (LR[0][0]==0) LRZerlegung(); s=1; for(int i=0;i<n;i++){ if (z[i]!=i) sig=-sig; s*=sig*LR[this.z[i]][i]; } //for i }// if else System.out.println("Die Matrix ist keine quadratische Matrix"); return s; } //ende Determinante }// ende class Matrix class TridiagonalMatrix extends Matrix{ private double[] a,b,c,r,l; //Die Diagonalen public TridiagonalMatrix(int n){ super(n); //Konstruktor der Superklasse a=new double[n]; //Diagonale b=new double[n-1];//Superdiagonale, bleibt nach LR c=new double[n];//Subdiagonale c[0] unbenutzt r=new double[n];//Diagonale von R l=new double[n];//Subdiagonale von L }//Ende Konstruktor public void setDaten(double[] a, double[] b, double[] c){ int i; for (i=0;i<n-1;i++){ this.a[i]=a[i]; this.b[i]=b[i]; this.c[i]=c[i]; }//Ende for this.a[n-1]=a[n-1]; this.c[n-1]=c[n-1]; for(i=0;i<n-1;i++){ this.Data[i][i+1]=b[i]; this.Data[i][i]=a[i]; this.Data[i+1][i]=c[i+1]; } //for this.Data[n-1][n-1]=a[n-1]; }//Ende SetDaten public void LRZerlegung(){//Redefinition r[0]=a[0];LR[0][0]=r[0]; for (int i=1;i<n;i++){ l[i]=c[i]/r[i-1]; LR[i][i-1]=l[i]; r[i]=a[i]-l[i]*b[i-1]; LR[i][i]=r[i]; LR[i-1][i]=b[i-1]; }//for }//Ende LRZerlegung() public double[] LoeseLGS(double[] d){//Redefinition int i; double[] x; if (LR[0][0]==0) LRZerlegung(); for(i=1;i<n;i++) d[i]-=l[i]*d[i-1]; x=new double[n]; x[n-1]=d[n-1]/r[n-1]; for(i=n-2;i>=0;i--) x[i]=(d[i]-b[i]*x[i+1])/r[i]; return x; }//Ende LoeseLGS }//Ende Class TridiagonalMatrix
Matrix, TridiagonalMatrix
, aber auch
die von mir geschriebene Klasse Tools
nutzt.
nutzt:
class TestMatrix { //Hauptprogramm zur Verwendung der Klassen //Matrix und TridiagonalMatrix public static void main(String args[]){ double[][] a={{2.1,-1,1},{2,-4,3},{-4,8,-4}}; int n=3; Matrix A=new Matrix(n,a); //Konstruktor double[] b={3.1, 3, 0}; Tools.SetmeinOut("Ergeb.txt");//Alle folgenden Ausgaben werden in //eine Datei namens "Ergeb.txt" geschrieben Tools.meinOut.println("Eingabematrix:"); Tools.Schreibe(a,n,n,4,8); //4 Vorkomma-, 8 Nachkommastellen Tools.meinOut.println(); double[] x=A.LoeseLGS(b); Tools.meinOut.println("Lösungsvektor:"); Tools.Schreibe(x,n,4,2); //4 Vorkomma-, 2 Nachkommastellen Tools.meinOut.println("Spur: "+A.Spur()+", Det: "+A.Determinante()); Tools.meinOut.println(); double[][] LR; int[] p; //Permutationsvektor fuer LR-Zerlegung A.GetLR(LR=new double[n][n], p=new int[n]); Tools.meinOut.println("Permutationsvektor:"); Tools.Schreibe(p,n); Tools.meinOut.println("LR-Zerlegung ergibt:"); Tools.Schreibe(LR,n,n,4,8); Tools.meinOut.println("Und nun das Ganze mit einer Tridiagonalmatrix:"); double[] bT={-1,-1}; double[] aT={2,2,2}; double[] cT={0,-1,-1}; double[] dT={1,0,1}; //rechte Seite TridiagonalMatrix T=new TridiagonalMatrix(n); T.setDaten(aT,bT,cT); Tools.meinOut.println("Eingabematrix:"); Tools.Schreibe(T.Data,n,n,4,2); double[] xT=new double[3]; xT=T.LoeseLGS(dT); //wird automatisch eine LR-Zerlegung gemacht Tools.meinOut.println("L”sungsvektor:"); Tools.Schreibe(xT,n,4,3); Tools.meinOut.println("Spur: "+T.Spur()+", Det: "+T.Determinante()); Tools.meinOut.println(); Tools.meinOut.println("LR-Zerlegung ergibt:"); Tools.Schreibe(T.LR,n,n,4,8); //und jetzt ohne Ausnutzen der Tridiagonalstruktur: Matrix B=new Matrix(n,T.Data); double[] dB={1,0,1}; double[] xB=B.LoeseLGS(dB); Tools.meinOut.println("Jetzt ohne Ausnutzung der Tridiagonalstruktur:"); Tools.Schreibe(B.Data,n,n,4,2); Tools.meinOut.println("Lösungsvektor:"); Tools.Schreibe(xB,n,4,3); Matrix C=new Matrix(n); C=Lin.Addition(A,B);//Polymorphie! Da hier B als Matrix uebergeben wird Tools.meinOut.println("Matrizensumme:"); Tools.Schreibe(C.Data,n,n,4,2); }//ende main }// ende class TestMatrix
java.lang
,
andere wie java.applet
oder java.awt
(siehe Applets)
müssen importiert werden.
OOP mit der Klasse Funktion und der Unterklasse Polynom