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.
class 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
Die Werte von Methoden werden mit Hilfe des
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:
class Matrix{
...
...
//Konstruktor:
public Matrix(int m, int n, double[][] Data){
this.m=m;
this.n=n;
this.Data=Data;
}
...
...
}//Ende class Matrix
Die Matrix wird "konstruiert", wenn ihre Abmessungen und
ihre Elemente
übergeben werden. Hier ist die Verwendung des Zeigers 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.
//Konstruktor:
public Matrix(int nZeile, int nSpalte, double[][] a){
m=nZeile;
n=nSpalte;
Data=a;
}
Die Verwendung von 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
public Matrix(int n, double[][] Data){
this(n,n,Data);
}
überladen werden, indem der erste Konstruktor
mit Hilfe des Zeigers 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:
class TridiagonalMatrix extends Matrix{
...
...
}
Mit dem Zusatz
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:
class 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
Nun stehen einem Objekt der Klasse 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:
class 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
Die Methode 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