Programmieren mit C++

Code-Beispiele & Lösungen

Klassen für allgemeine Aufgaben

Die Matrix-Klasse

Die hier vorgestellte Matrix-Klasse wurde in ANSI-kompatiblem C++ unter Verwendung des Borland-C++-Compilers implementiert. Übertragungen auf andere C++-Dialekte sollten jedoch keine Schwierigkeiten bereiten.

Um die Übersichtlichkeit zu erhalten und Kompilationszeiten beim Austesten und Verändern zu verkürzen, wurde die Matrix-Klasse in das Headerfile MATRIZEN.H und das Modul MATRIZEN.CPP unterteilt.

Voraussetzung für eine effiziente Nutzung von Matrizen und deren Manipulation auf dem Computer ist die Existenz einer möglichst einfach zu handhabenden Matrizen-Arithmetik, die den Anwender von den mathematischen Besonderheiten in der Matrizenrechnung befreit. Die geeignetste Form stellt dabei die symbolische Schreibweise dar. Ausdrücke wie z. B.

A = B + C * INV(D) - TRANS(E)

erlauben es, den programmtechnischen Hintergrund zu ignorieren und sich voll auf das zugrunde liegende mathematische Problem zu konzentrieren.

Um für die nachfolgenden Erläuterungen einen einheitlichen Sprachgebrauch sicherzustellen, werden hier kurz die Eigenschaften von Matrizen zusammengefaßt.

Einsatz von Matrizen

Eine Matrix A ist ein 2dimensionales Zahlenschema, das aus m Zeilen und n Spalten besteht. Die einzelnen Zahlenwerte a(i,j) {i=1, … ,m ; j=1, … ,n} in einer Matrix heißen Elemente, wobei der erste Index immer die Zeilenposition und der zweite Index immer die Spaltenposition eines Elements angibt. Die Größe einer (m x n)-Matrix, also die Anzahl der Zeilen und Spalten, wird in der Mathematik als Ordnung oder Dimension bezeichnet. Sonderfälle von Matrizen sind Zeilen- und Spaltenvektoren, die nur aus einer Zeile bzw. einer Spalte bestehen, und die Skalare, das sind Matrizen, die nur ein Element besitzen.

Definition von Matrizen

Die gebräuchlichsten Schreibweisen bzw. Abkürzungen für Matrizen sind: (aij)mn, A(mn), (aij) oder einfach nur A. Die Schreibweise mit Großbuchstaben für Matrizen und Kleinbuchstaben für Vektoren soll im folgenden verwandt werden.

Schreibweisen für Matrizen

Die Matrix-Arithmetik lehnt sich zwar stark an die „normale“ Arithmetik an, besitzt jedoch einige Besonderheiten. So setzen z. B. die Addition (A+B) und die Subtraktion (A-B), bei der jedes Element der ersten Matrix zu dem Element der zweiten Matrix addiert bzw. subtrahiert wird, Matrizen gleicher Dimension voraus. Bei der Multiplikation zweier Matrizen (A*B) muß dagegen darauf geachtet werden, daß die Anzahl der Spalten der ersten Matrix mit der Anzahl der Zeilen der zweiten Matrix übereinstimmt.

Matrix-Arithmetik

Nun zu den prinzipiellen Überlegungen, die bei der programmtechnischen Umsetzung der Matrizen-Arithmetik in eine Matrix-Klasse angestellt wurden. Grundvoraussetzung für die Implementation einer symbolischen Matrix-Arithmetik ist die Fähigkeit von C++-Operatoren, zu überladen. Das Operator-Overloading ermöglicht es, über die Standard-Operatoren +, -, *, = usw. Operationen für selbstdefinierte Datentypen, wie in unserem Fall die Matrizen, zu legen. Diese generischen Operatoren verdecken nun einerseits die Komplexität der darunterliegenden Funktionen (d. h. Methoden), andererseits erlauben sie es, die natürliche mathematische Schreibweise zu benutzen. So haben z. B. die Multiplikationsfunktion für Integerwerte (int C=A*B) und die Multiplikationsfunktion für Matrizen (Matrix A=B*C) dieselbe Schreibweise, hinter den beiden *-Operatoren verbirgt sich jedoch etwas ganz Verschiedenes, wie sich noch zeigen wird.

Die Klassenimplementierung

Programmtechnische Umsetzung

Dem Funktionsbibliotheken-Konzept von C++ folgend, wurden die Deklarationen der Matrixklasse in ein Headerfile und die Implementation der Methoden der Klasse in ein Object-Modul gefaßt. Die Headerdatei läßt sich in 3 Abschnitte unterteilen. Zunächst werden die benötigten Include-Dateien, ein Debug-Flag und die Liste der Fehlermeldungen der Matrixklasse definiert. Das Debug-Flag kann und sollte dazu benutzt werden, sich Klarheit zu verschaffen, wie die Matrixklasse Speicher allokiert und deallokiert, wie Methoden auf andere Methoden zugreifen und wie der besonders interessante Copy-Initializer und der Zuweisungsoperator arbeiten.

Code-Aufteilung

Die Objekte (Matrizen) der Matrixklasse besitzen eine relativ einfache Datenstruktur. Neben der Anzahl der Zeilen und Spalten enthält sie lediglich einen Zeiger auf ein Array von double-Werten. Dieses Array, das die einzelnen Elemente einer Matrix aufnimmt, muß, wenn der Anwender oder eine Methode der Klasse eine Matrix erzeugen will, entsprechend eingerichtet und der benötigte Speicher allokiert werden. Diese Aufgabe übernehmen die Konstruktoren. Verläßt ein Matrix-Objekt den Sichtbarkeitsbereich, so wird intern der Destruktor aufgerufen, der den zuvor allokierten Speicher wieder frei gibt.

Struktur und Erzeugung einer Matrix

Im folgenden sollen die Methoden der Matrixklasse erläutert werden. Dabei sollen allerdings nicht ausschließlich die mathematischen Grundlagen der Methoden im Vordergrund stehen, sondern auch die programmtechnischen Besonderheiten.

Konstruktoren

Methoden der Matrixklasse

Matrix::Matrix( void )

C++ erstellt bei benutzerdefinierten Datenstrukturen normalerweise automatisch einen Standard-Konstruktor, der Speicherplatz für das zu erzeugende Objekt allokiert und die Daten, hier Zeilen- und Spaltenanzahl sowie den Zeiger auf das Array, auf Null setzt. Letzteres machen nicht alle C++-Compiler selbständig. Daher müssen die Daten auf Null gesetzt werden, was der Konstruktor Matrix::Matrix() erledigt.

Standardkonstruktor

Matrix::Matrix( char file[] )

Der Konstruktor Matrix::Matrix erzeugt ein Matrixobjekt aus einer Datei, die in einem standardisierten Format vorliegen muß. Eine solche Datei muß eine ASCII-Datei sein, in der auf beliebig viele Zeilen Kommentar (der mit „//“ eingeleitet werden muß) die Dimension der Matrix in einer der beiden Formen „zeilen=3 spalten=5“ oder „z=3 s=5“ folgen muß. Daran muß sich das Wort „“ anschließen. Hierauf beginnt der eigentliche Datenabschnitt der Datei, in dem die einzelnen Werte zeilenweise und durch Leerzeichen voneinander getrennt stehen müssen. Am besten schauen Sie sich ein solches File selbst einmal in einem Editor an.

Konstruktor Matrix::Matrix

Matrix::Matrix(int Zeilenanzahl, 
               int Spaltenanzahl )

Der Null-Konstruktor erzeugt eine Matrix der Dimension SpaltenzahlxZeilenzahl und initialisiert alle Elemente mit dem Wert 0.

Null-Konstuktor

Matrix::Matrix( Matrix & M )

Der Copy-Konstruktur erzeugt eine Matrix, indem er die übergebene Matrix M dupliziert.

Copy-Konstruktor

Matrix::Matrix(int Zeilenanzahl,
               int Spaltenanzahl,
               double Wert[])

Mit dem Listen-Konstruktor kann eine Matrix erzeugt und aus einer Liste von Werten initialisiert werden. Wert[] enthält die Daten als eindimensionale Liste von Element 0 bis Element Zeilenanzahl*Spaltenanzahl-1.

Listen-Konstruktor

Matrix::Matrix (int Dimension )

Der Einheits-Konstruktor erzeugt eine Einheitsmatrix der Größe Dimension.

Einheits-Konstruktor

Matrix::Matrix (int Dimension, 
                double Wert )

Dieser Konstruktor liefert eine quadratische Matrix der größe Dimension x Dimension und besetzt alle Elemente mit Wert.

Konstruktor quadratrische Matrix

Matrix::~Matrix()

Der Destruktor zerstört ein Matrixobjekt und gibt den allokierten Speicher, der in den Konstruktoren durch new erzeugt wurde, mit delete wieder frei.

Die Operatoren +, -, *

Um die Standard-Operatoren auch für Matrizen zugänglich zu machen, wurden diese mit entsprechenden matrizenspezifischen Methoden überladen. Diese Operatoren sind entweder als Member- oder als Friend-Funktionen ausgeführt. Die Member-Funktionen beziehen sich direkt auf ein Matrixobjekt und benötigen daher im Funktionsaufruf nur noch das zweite Argument. Die friend-Funktionen hingegen benötigen für binäre Operatoren sowohl die linke als auch die rechte Seite des Operators. Die friend-Funktionen sind hier deshalb erforderlich, weil sich Member-Funktionen immer auf ein Matrixobjekt beziehen müssen. Damit wäre z. B. die eine neue Matrix erzeugende Funktion A*5 realisierbar, 5*A jedoch nicht. In solchen Fällen müssen also friend-Funktionen benutzt werden, um beide Möglichkeiten zu gewährleisten.

Destruktor

Die Matrix-Klasse unterstützt die folgenden Funktionen:

  • Der Zuweisungsoperator (=), der die Matrix oder das Ergebnis der rechten Seite in das linke Matrixobjekt kopiert. Die Dimension der linken Seite spielt dabei keine Rolle, da diese automatisch auf die richtige Dimension gebracht wird.
  • Die Zuweisungsoperatoren (+=, -=, *=), die zur linken Seite des Operators die rechte Seite addieren, subtrahieren oder multiplizieren. Hierbei müssen die Dimensionen allerdings übereinstimmen. Zusätzlich wurde die Möglichkeit eingebunden, diese Operationen mit double-Werten als rechte Seite zu nutzen. Dabei wird jedes Element der Matrix auf der linken Seite mit dem entsprechenden double-Wert addiert, subtrahiert oder multipliziert. Dies ist zwar mathematisch nicht korrekt, erleichtert aber die Arbeit mit den Matrizen.
  • Die GrundOperatoren (+, -, *), die entweder zwei Matrizen, einen double-Wert und eine Matrix oder eine Matrix und einen double-Wert miteinander verknüpfen; das zurückgelieferte Argument ist in jedem Fall eine Matrix. Werden zwei Matrizen miteinander verknüpft, ist auf entsprechende Dimensionen zu achten.
  • Das unäre Minus (-), das die Vorzeichen aller Elemente einer Matrix umdreht.

Matrizenspezifische Funktionen

Implementierte Funktionen

Die mathematische Herleitung und der Aufbau der Algorithmen der oben genannten Funktionen sollen hier unterbleiben. Für Interessierte sei auf die vielfältige Literatur zum Thema Matrizenrechnung verwiesen.

Verwendete Algorithmen

An dieser Stelle sei auch darauf hingewiesen, daß das Verhalten der Algorithmen in Extremsituationen aufgrund der beschränkten Rechengenauigkeit von Computern von der wahren Lösung abweichen kann. So ergab z. B. die Berechnung einer Inversen einer Matrix, deren Determinante < 1.0-E16 war, in 3 verschiedenen Mathematikprogrammen unterschiedliche Ergebnisse! Ergebnisse numerischer Berechnungen sollte der Anwender dieser Funktionen immer kritisch betrachten.

Vorsicht in Extremsituationen

Matrix trans( Matrix & M )

trans berechnet die Transponierte einer Matrix A=trans(B). Die Funktion benötigt als Parameter die Matrix, deren Transponierte bestimmt werden soll. Das Ergebnis der Funtion ist die transponierte Matrix.

Funktion trans

int rang( Matrix & M )

rang benötigt als Parameter eine Matrix. Die Funktion liefert als Ergebnis den Rang der Matrix r=rang(A) als Integerwert zurück.

Funktion rang

double det( Matrix & M )

Mit det kann die Determinante der Matrix, die als Parameter übergeben wird, ermittelt werden. Das Funktionsergebnis ist vom Typ double.

Funktion det

Matrix inv( Matrix & M )

inv bestimmt die Inverse der Matrix. Die Funktion. benötigt hierzu als Parameter die Matrix, deren Inverse ermittelt werden soll, und gibt ein Ergebnis vom Typ Matrix zurück.

Funktion inv

Matrix solve(Matrix &M, Matrix &B)

solve löst ein lineares Gleichungssystem der Form A*x=b. Allerdings ist der Algorithmus, der sich der Inversen bedient, nur in der Lage, eindeutige Lösungen zu berechnen.

Manipulationsfunktionen

Um bestehende Matrixobjekte während der Laufzeit außerhalb der mathematischen Funktionen zu verändern, wurden zusätzliche Methoden implementiert.

Funktion solve

void put(Matrix &M, 
         int zpos, int spos, 
Matrix &A)

put setzt in eine Matrix eine andere Matrix ein. In die Matrix, die als erster Parameter übergeben wird, wird ab Zeile zpos und Spalte spos die Matrix A eingesetzt. Die Funktion liefert kein Ergebnis zurück.

Funktion put

void get(Matrix &M,
         int zpos,int spos,
         int zeilen,int spalten,
         Matrix &A)

get extrahiert aus einer Matrix eine Teilmatrix, indem beginnend bei Element (zpos, spos) eine Teilmatrix der Größe spalten x zeilen nach A kopiert wird. Ist der durch spalten x zeilen gegebene Bereich größer als der ab (zops, spos) verfügbare Restbereich der Matrix M, wird lediglich der gültige Bereich kopiert. Das Funktionsergebnis ist vom Typ void.

Funktion get

Matrix zvektor(Matrix &M,int zeile)

Die Funktion zvektor liefert einen Zeilenvektor der Matrix M, wobei zeile den Index der gewünschten Matrixzeile angibt. Das Funktionsergebnis ist vom Typ Zeilenvektor.

Funktion zvektor

Matrix svektor(Matrix &M,int spalte)

svektor liefert einen Spaltenvektor der Matrix M, wobei spalte den Index der gewünschten Matrixspalte angibt. Das Funktionsergebnis ist vom Typ Spaltenvektor.

Informationsfunktionen

Funktion svektor

Ebenfalls einfach zu verstehen und doch sehr hilfreich sind die Methoden, die Informationen über die Gestalt und Struktur einer Matrix liefern.

Zeilen() und Spalten() liefern die Dimension einer Matrix zurück.

Struktur einer Matrix

int operator == ( Matrix & A , Matrix & B )

== vergleicht Matrix A mit Matrix B auf Gleichheit aller Elemente. Diese Operation ist nur dann erlaubt, wenn beide Matrizen die gleiche Dimension haben. Sind die Matrizen gleich, lautet das Funktionsergebnis 1, ansonsten 0.

Operator ==

int operator == ( Matrix & A , Matrix & B )

!= prüft die beiden als Parameter übergebenen Matrizen A und B auf Ungleichheit. Die Operatorfunktion liefert 1, wenn die Matrizen nicht identisch sind, sonst 0.

Operator !=

double minimum(Matrix &M, 
               int &zeile, int &spalte)
double maximum(Matrix &M, 
               int &zeile,int &spalte)

minimum und maximum liefern den kleinsten bzw. größten in der Matrix M vorkommenden Wert und geben dessen Position in zeile und spalte zurück.

Funktionen minimum maximum

double min( Matrix & M )
double max( Matrix & M )

min und max ermitteln den kleinsten und größten Wert der matrix M ohne jedoch dessen Position zurückzuliefern.

Funktionen min und max

double mean( Matrix & M )

mean liefert den Mittelwert der Matrix als Funktionsergebnis.

Funktion mean

double varianz( Matrix & M )

varianz bildet die Varianz der Matrix M und liefert deren Wert als Funktionsergebnis zurück.

Ausgabefunktionen

Funktion varianz

Zur Ausgabe der Matrizen wurden zwei Print-Methoden implementiert. Deren Einsatz verbietet sich jedoch unter grafischen Oberflächen wie Windows oder OS/2.

Print-Methoden

void print( Matrix & M )

Die Funktion print gibt die Elemente der Matrix M ohne jede Formatierung auf dem Bildschirm aus.

Unformatierte Ausgabe

void print(char *FORMAT , Matrix & M )

Die zweite print-Funktion gibt alle Elemente der Matrix M unter Verwendung der Formatanweisung FOTMAT aus.

Formatierte Ausgabe

Die nachfolgende Anweisung formatiert die Matrixelemente auf 5 Dezimalstellen:

print("%5.5f",A)

Beispiel

void save(Matrix & M, 
          char *filename, 
          char *anmerkung)

save() gibt eine Matrix in einem standardisierten Format als File aus, das später über den Konstruktor Matrix(char FILE) wieder zu einem Matrixobjekt umgewandelt werden kann.

Funktion save





Sachgebiet


© 2009-2012 by Alojado Publishing. Alle Rechte vorbehalten. Ausgewiesene Marken gehören ihren jeweiligen Eigentümern.
Mit der Benutzung dieser Seite erkennen Sie die Nutzungsbedingungen und die Datenschutzerklärung an. Der Betreiber übernimmt keine Haftung für den Inhalt verlinkter externer Internetseiten.
Seite erzeugt 2012-05-20 01:48:29 von textarchiv.alojado.de