06.10.2013 Aufrufe

Kapitel 2 Matrizen in C++

Kapitel 2 Matrizen in C++

Kapitel 2 Matrizen in C++

MEHR ANZEIGEN
WENIGER ANZEIGEN

Erfolgreiche ePaper selbst erstellen

Machen Sie aus Ihren PDF Publikationen ein blätterbares Flipbook mit unserer einzigartigen Google optimierten e-Paper Software.

<strong>Kapitel</strong> 2<br />

<strong>Matrizen</strong> <strong>in</strong> <strong>C++</strong><br />

In der Computersprache C ist die Standardmethode zur Behandlung von <strong>Matrizen</strong> durch<br />

1 const <strong>in</strong>t n=10;<br />

2<br />

3 double a[n][n];<br />

gegeben. Allerd<strong>in</strong>gs gibt es bei dieser Methode e<strong>in</strong>e Reihe von Schwierigkeiten, <strong>in</strong>sbesondere<br />

wenn wir das Array a an e<strong>in</strong>e Funktion übergeben.<br />

E<strong>in</strong>e deutlich zufriedenstellendere Lösung kann mit Hilfe des Klassenkonzeptes erzielt<br />

werden. In den folgenden Abschnitten wollen wir e<strong>in</strong>e Matrixklasse programmieren. Diese<br />

Klasse wird Ausgangspunkt für die meisten Programme dieses Kurses darstellen.<br />

2.1 Die Matrixklasse matrix<br />

Beg<strong>in</strong>nen wir damit, die wichtigsten Elemente dieser Matrixklasse anzuführen. Der E<strong>in</strong>fachheit<br />

halber wollen wir nur reelle <strong>Matrizen</strong> (Variablentyp double) der Größe n × n<br />

(Ordnung n) betrachten.<br />

Zuerst soll es möglich se<strong>in</strong>:<br />

• e<strong>in</strong>e Matrix e<strong>in</strong>er bestimmten Größe zu erstellen (d.h., genügend Speicherplatz für<br />

die n × n Matrixelemente zu reservieren);<br />

• auf die Matrixelemente zuzugreifen (d.h., sie zu lesen oder zu verändern).<br />

E<strong>in</strong> typisches Programm könnte dann von der Form<br />

7


8 KAPITEL 2. MATRIZEN IN <strong>C++</strong><br />

1 matrix a(3); // 3 x 3 Matrix<br />

2<br />

3 a(1,2)=1;<br />

4 a [0][1]=2;<br />

se<strong>in</strong>. In der ersten Zeile wird e<strong>in</strong>e 3×3 Matrix deklariert, wobei an dieser Stelle genügend<br />

Speicherplatz reserviert werden soll. Zeile 3 zeigt e<strong>in</strong>en Zugriff auf das Matrixelement a12.<br />

Weiters wäre es wünschenswert, dass wir auch <strong>in</strong> “klassischer” C-Form a[ i ][ j ] auf das<br />

Matrixelement aij zugreifen können.<br />

Wir beg<strong>in</strong>nen nun mit der Implementierung der Matrixklasse. Zuerst öffnen wir zwei Dateien<br />

matrix.h und matrix.cc. matrix.h ist die header-Datei, <strong>in</strong> der die Klassendef<strong>in</strong>ition<br />

(sowie kürzere Klassenfunktionen) stehen sollen. In matrix.cc werden die längeren<br />

Klassenfunktionen ausprogrammiert.<br />

Im Folgenden soll angenommen werden, dass Sie die Grundkonzepte von Klassen <strong>in</strong> <strong>C++</strong><br />

kennen und wissen, wie Sie mehrere Dateien kompilieren und b<strong>in</strong>den können (z.B. unter<br />

Verwendung von Makefile).<br />

2.1.1 Grundelemente der Matrixklasse<br />

Beg<strong>in</strong>nen wir mit der Klasse matrix.h. Das Gerüst ist von der Form<br />

1 #ifndef matrix h<br />

2 #def<strong>in</strong>e matrix h<br />

3<br />

4 class matrix {<br />

5 public:<br />

6 matrix(<strong>in</strong>t n=0);<br />

7 ˜matrix();<br />

8<br />

9 double& operator() (<strong>in</strong>t i, <strong>in</strong>t j);<br />

10 double ∗operator[] (<strong>in</strong>t i);<br />

11<br />

12 <strong>in</strong>t size () const {return n; }<br />

13<br />

14 private:<br />

15 <strong>in</strong>t n;<br />

16 double ∗a;<br />

17 };<br />

18<br />

19 #endif<br />

Die Bedeutung der Zeilen ist wie folgt:


2.1. DIE MATRIXKLASSE matrix 9<br />

Zeilen 1, 2, 19. Die übliche Struktur von header files, die verh<strong>in</strong>dert, das e<strong>in</strong>e header-<br />

Datei öfters als e<strong>in</strong>mal e<strong>in</strong>gelesen wird.<br />

Zeile 6. Konstruktor für die Matrix der Ordnung n.<br />

Zeile 7. Destruktor.<br />

Zeile 9. Zugriff auf Matrixelemente über a(i , j).<br />

Zeile 10. Zugriff auf Matrixelemente über a[ i ][ j ].<br />

Zeile 12. Funktion, die die Ordnung n der Matrix liefert.<br />

Zeile 15. Ordnung n der Matrix (d.h., n × n-Matrix).<br />

Zeile 16. Zeiger auf Matrixelemente.<br />

2.1.2 Speicherung der Matrixelemente<br />

Betrachten wir die Matrix<br />

A =<br />

⎛<br />

⎜<br />

⎝<br />

a00 a01 a02 a03<br />

a10 a11 a12 a13<br />

a20 a21 a22 a23<br />

a30 a31 a32 a33<br />

wobei wir von der C-Notation Gebrauch gemacht haben, dass Indizes mit 0 beg<strong>in</strong>nen.<br />

Wie können wir diese 4 × 4 Matrix abspeichern?<br />

E<strong>in</strong>e Möglichkeit besteht dar<strong>in</strong>, die Matrixelemente h<strong>in</strong>tere<strong>in</strong>ander <strong>in</strong> e<strong>in</strong>em Vektor der<br />

Länge n × n = 16 zu speichern<br />

{a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33}.<br />

Konstruktor und Destruktor.<br />

Im Konstruktor der Matrixklasse müssen wir somit e<strong>in</strong>en Speicherplatz der Länge n × n<br />

reservieren, der dann vom Destruktor wieder freigegeben wird<br />

6 matrix(<strong>in</strong>t n=0) : n( n ) { if (n) a=new double[n∗n]; }<br />

7 ˜matrix() { if (n) delete[] a; }<br />

operator().<br />

Wie greifen wir auf das Matrixelement aij zu? Offensichtlich über a[ i∗n+j].<br />

Wir können somit die Klassenfunktion operator() implementieren. Weil sie so kurz ist,<br />

können wir sie gleich nach matrix.h schreiben. Zuerst fügen wir vor der Klassendef<strong>in</strong>ition<br />

die Zeile<br />

⎞<br />

⎟<br />


10 KAPITEL 2. MATRIZEN IN <strong>C++</strong><br />

#def<strong>in</strong>e <strong>in</strong>dex(i,j) (( i)∗n+(j))<br />

e<strong>in</strong>. Und weiters<br />

9 double& operator() (<strong>in</strong>t i, <strong>in</strong>t j) { return a[<strong>in</strong>dex(i,j )]; }<br />

operator[].<br />

Schließlich wollen wir noch den normalen Indexoperator implementieren. Wie gehen wir<br />

vor?<br />

Wenn wir wollen, dass wir mit Hilfe von a[ i ][ j ] auf das Matrixelement aij e<strong>in</strong>er Matrix<br />

A zugreifen können, so muss operator[i] e<strong>in</strong>en Zeiger liefern. Betrachten wir nochmals<br />

die Aufteilung des Speicherplatzes<br />

a a+n a+2∗n a+3∗n<br />

↓ ↓ ↓ ↓<br />

{ a00, a01, a02, a03, a10, a11, a12, a13, a20, a21, a22, a23, a30, a31, a32, a33 }<br />

Wir erkennen, dass der Zeiger a+i∗n auf den Beg<strong>in</strong>n der i-te Zeile der Matrix A zeigt.<br />

Durch (a+i∗n)[j] können wir also auf aij zugreifen.<br />

Somit können wir die Klassenfunktion<br />

10 double& operator[] (<strong>in</strong>t i) { return a+i∗n; }<br />

vervollständigen.<br />

Aufgabe 2.1— Vervollständigen Sie die Matrixklasse. Schreiben Sie e<strong>in</strong> Programm,<br />

das e<strong>in</strong>e 3 × 3-Matrix deklariert und sie mit den Matrixelementen<br />

besetzt.<br />

⎛<br />

A = ⎝<br />

1 2 0<br />

2 4 3<br />

7 2 1<br />

Aufgabe 2.2— Betrachten Sie die folgende Funktion:<br />

1 void f(const matrix& a)<br />

2 {<br />

3 cout


2.2. ALGORITHMEN 11<br />

2.2 Algorithmen<br />

In diesem Abschnitt wollen wir e<strong>in</strong>ige nützliche Funktionen der <strong>C++</strong>-Standardbibliothek<br />

besprechen, die <strong>in</strong> den header-Dateien<br />

#<strong>in</strong>clude <br />

#<strong>in</strong>clude <br />

def<strong>in</strong>iert s<strong>in</strong>d.<br />

Ausgansgpunkt s<strong>in</strong>d die sogenannten Iteratoren. Das s<strong>in</strong>d beliebige Objekte, für die gelten<br />

soll, dass sie:<br />

• über den Zuordnungsoperator operator= auf e<strong>in</strong>en bestimmten Anfangswert gesetzt<br />

werden können;<br />

• über die Vergleichsoperatoren operator== und operator!= mit e<strong>in</strong>em anderen Iterator<br />

verglichen werden können;<br />

• über operator++ <strong>in</strong>krementiert werden können;<br />

• über operator∗ e<strong>in</strong>en Wert liefern.<br />

Offensichtlich s<strong>in</strong>d Zeiger Iteratoren, da alle Anforderungen<br />

1 double a[10],∗p;<br />

2<br />

3 for (p=a; p!=a+n; p++) ∗p=0;<br />

erfüllt s<strong>in</strong>d (es gibt allerd<strong>in</strong>gs noch e<strong>in</strong>e Reihe weiterer Iteratoren, die Zugriff auf verschiedenst<br />

Objekte —z.B. Conta<strong>in</strong>er— erlauben).<br />

Was können wir mit diesen Iteratoren machen? All die kle<strong>in</strong>en Aufgaben, die wir im<br />

Programmieralltag stets benötigen. In dem obigen Beispiel hätten wir auch<br />

1 fill (a,a+n,0.);<br />

schreiben können unter Benutzung der Funktion<br />

1 copy(InputIter first , InputIter last , const double& value);<br />

Hierbei wird der Speicherplatz <strong>in</strong> dem Bereich first bis last mit dem Wert value belegt.<br />

Zwei D<strong>in</strong>ge s<strong>in</strong>d zu beachten, die allgeme<strong>in</strong> für alle Funktionen von numeric und algorithm<br />

gelten:<br />

• es erfolgt ke<strong>in</strong>e Überprüfung, ob der Speicherplatz, auf den das Programm zugreift,<br />

zuvor reserviert wurde;


12 KAPITEL 2. MATRIZEN IN <strong>C++</strong><br />

• der letzte Iterator last zeigt auf das Element, das unmittelbar nach dem letzten<br />

Element steht, auf das zugegriffen werden soll (<strong>in</strong> dem obigen Beispiel a+n, d.h.<br />

das Element a[n]).<br />

Die am häufigsten verwendeten Funktionen s<strong>in</strong>d<br />

1 // aus numeric<br />

2 T accumulate(InputIter first , InputIter last , T <strong>in</strong>it );<br />

3 T <strong>in</strong>ner product(InputIter first1 , InputIter last1 , InputIter first2 , T <strong>in</strong>it );<br />

4<br />

5 // aus algorithm<br />

6 count(InputIter first , InputIter last , const T& value);<br />

7 copy(InputIter first , InputIter last , OutputIter result );<br />

8 bool equal(InputIter first1 , InputIter last1 , InputIter first2 );<br />

9 fill (OutputIter first , OutputIter last , const T& value);<br />

10 InputIter max element(InputIter first , InputIter last );<br />

11 InputIter max element(InputIter first , InputIter last );<br />

12 replace(ForwardIter first , ForwardIter last , const T& old value, const T& new value);<br />

13 reverse(ForwardIter first , ForwardIter last );<br />

14 sort(ForwardIter first , ForwardIter last );<br />

Hierbei bezeichnet T den Variablentyp (z.B. double), InputIter ist e<strong>in</strong> Iterator der nur<br />

gelesen wird, OutputIter e<strong>in</strong>er der verändert wird, und ForwardIter e<strong>in</strong>er der gelesen und<br />

verändert wird.<br />

Die Wirkung der Funktionen ist:<br />

accumulate: summiert den Inhalt von InputIter zu <strong>in</strong>it ;<br />

<strong>in</strong>ner product: bildet das <strong>in</strong>nere Produkt ab von zwei Vektoren a und b;<br />

count: zählt, wie oft value vorkommt;<br />

copy: kopiert e<strong>in</strong>en Bereich <strong>in</strong> e<strong>in</strong>en anderen;<br />

equal: überprüft zwei Bereiche auf Gleichheit;<br />

fill: kopiert den Wert value <strong>in</strong> alle Elemente e<strong>in</strong>es Bereiches;<br />

max element: liefert den Iterator auf das größte Element;<br />

m<strong>in</strong> element: liefert den Iterator auf das kle<strong>in</strong>ste Element;<br />

replace: ersetzt <strong>in</strong> e<strong>in</strong>em Bereich old value durch new value;<br />

reverse: kehrt den Inhalt e<strong>in</strong>es Bereiches um;<br />

sort: sortiert den Inhalt e<strong>in</strong>es Bereiches <strong>in</strong> aufsteigender Reihenfolge.<br />

Wann immer möglich, wollen wir <strong>in</strong> den folgenden Programmen von diesen Funktionen<br />

Gebrauch machen.


2.3. DIE VOLLSTÄNDIGE MATRIXKLASSE 13<br />

Aufgabe 2.3— Betrachten Sie das Programm<br />

1 #<strong>in</strong>clude <br />

2 #<strong>in</strong>clude <br />

3<br />

4 void ma<strong>in</strong>()<br />

5 {<br />

6 <strong>in</strong>t a [6]={0,1,2,0,3,2};<br />

7 <strong>in</strong>t b [6];<br />

8 }<br />

Benutzen Sie die zuvor e<strong>in</strong>geführten Funktionen und<br />

– kopieren Sie den Inhalt von a nach b;<br />

– überprüfen Sie die beiden Arrays auf Gleichheit;<br />

– drehen Sie die Reihenfolge von b um;<br />

– bestimmen Sie die Summe 5 i=0 ai;<br />

– bestimmen Sie das größte Element von b;<br />

– sortieren Sie a;<br />

– setzen Sie alle Elemente von b auf 0.<br />

2.3 Die vollständige Matrixklasse<br />

Damit wir die Funktionen von numeric und algorithm auch für die Matrixklasse verwenden<br />

können, führen wir zwei weitere Klassenfunktionen<br />

1 double ∗beg<strong>in</strong>() const { return a; }<br />

2 double ∗end() const { return a+n∗n; }<br />

e<strong>in</strong>, die auf den Beg<strong>in</strong>n bzw. das Ende des Matrixspeicherplatzes zeigen.<br />

Mit Hilfe dieser zusätzlichen Funktionen, können wir dann z.B. alle Matrixelement auf<br />

e<strong>in</strong>en bestimmten Wert setzen<br />

1 matrix a(5);<br />

2<br />

3 copy(a.beg<strong>in</strong>(),a.end (),0);<br />

Aufgabe 2.4— Schreiben Sie e<strong>in</strong> Programm, das den Inhalt e<strong>in</strong>er Matrix A<br />

<strong>in</strong> e<strong>in</strong>e zweite Matrix B kopiert.


14 KAPITEL 2. MATRIZEN IN <strong>C++</strong><br />

Für die häufigsten Matrixverknüpfungen —wie Zuordnung A = B, Addition A + B,<br />

Subtraktion A−B oder Multiplikation A∗B (entsprechend den Regeln der Matrixmultiplikation)—<br />

ist es vernünftig, die entsprechenden Klassenoperatoren zu implementieren.<br />

E<strong>in</strong>e solche Implementierung ist <strong>in</strong> den Dateien<br />

http://physik.uni-graz.at/~uxh/l<strong>in</strong>eare-algebra/matrix.h<br />

http://physik.uni-graz.at/~uxh/l<strong>in</strong>eare-algebra/matrix.cc<br />

zu f<strong>in</strong>den. Die vollständige Klasse be<strong>in</strong>haltet folgende Funktionen:<br />

1 class matrix {<br />

2<br />

3 public:<br />

4 matrix(<strong>in</strong>t n);<br />

5 matrix(const matrix& m);<br />

6 matrix(const char ∗filename);<br />

7 ˜matrix();<br />

8<br />

9 double ∗opertor[] (<strong>in</strong>t i );<br />

10 const double ∗operator[] (<strong>in</strong>t i) const;<br />

11 double& operator() (<strong>in</strong>t i, <strong>in</strong>t j);<br />

12 double operator() (<strong>in</strong>t i, <strong>in</strong>t j ) const;<br />

13<br />

14 const matrix& operator= (const matrix& m);<br />

15<br />

16 const matrix& operator+= (const matrix& m);<br />

17 const matrix& operator−= (const matrix& m);<br />

18 const matrix& operator∗= (const matrix& m);<br />

19<br />

20 matrix operator+ (const matrix& m) const;<br />

21 matrix operator− (const matrix& m) const;<br />

22 matrix operator∗ (const matrix& m) const;<br />

23<br />

24 double ∗beg<strong>in</strong>() const;<br />

25 double ∗end() const;<br />

26<br />

27 <strong>in</strong>t size ();<br />

28 void write();<br />

29 };<br />

Die Wirkung der Funktionen ist wie folgt:<br />

Zeile 4. Standardkonstruktor.<br />

Zeile 5. Konstruktor, wobei die Matrix dieselbe Ordnung n und Matrixelemente wie m<br />

erhält.


2.3. DIE VOLLSTÄNDIGE MATRIXKLASSE 15<br />

Zeile 6. Konstruktor, bei dem die Ordnung n und Matrixelemente aus e<strong>in</strong>er Datei<br />

filename e<strong>in</strong>gelesen werden. Der erste E<strong>in</strong>trag <strong>in</strong> der Datei soll die Ordnung n<br />

se<strong>in</strong>, gefolgt von den n × n Matrixelementen. Beispielsweise erhält mit der Datei<br />

matrix.dat<br />

1 3<br />

2 2 3 4<br />

3 1 4 0<br />

4 8 7 1<br />

die Matrix a <strong>in</strong> dem Programm<br />

1 matrix a(”matrix.dat”);<br />

die Form<br />

⎛<br />

⎝<br />

2 3 4<br />

1 4 0<br />

8 7 1<br />

⎞<br />

⎠.<br />

Zeilen 9–12. Zugriff auf Matrixelement über a(i , j) und a[ i ][ j ].<br />

Zeile 14. Zuordnungsoperator, der es erlaubt, Ordnung n und Inhalt e<strong>in</strong>er Matrix m <strong>in</strong><br />

die aktuelle Matrix zu kopieren.<br />

Zeilen 16–22. Addition, Subtraktion und Multiplikation (entsprechend cij = <br />

k aikbkj)<br />

von <strong>Matrizen</strong>; z.B.<br />

1 matrix a(3), b(3), c (3);<br />

2<br />

3 a+=b; c=a+b;<br />

4 a−=b; c=a−b;<br />

5 a∗=b; c=a∗b;<br />

Bei all diesen Operationen wird überprüft, ob die <strong>Matrizen</strong> dieselbe Ordnung n<br />

haben.<br />

Zeilen 24, 25. Funktionen, die den Beg<strong>in</strong>n beg<strong>in</strong>() bzw. das Ende end() des Speicherplatzes<br />

liefern, an dem die Matrixelemente abgespeichert s<strong>in</strong>d.<br />

Zeile 27. Ordnung n der Matrix.<br />

Zeile 28. E<strong>in</strong>fache Ausgaberout<strong>in</strong>e für Matrix.<br />

Aufgabe 2.5— Erstellen Sie zwei Dateien matrix.dat und diag.dat mit<br />

Inhalt<br />

⎛<br />

⎝<br />

2 3 4<br />

1 4 0<br />

8 7 1<br />

⎞<br />

⎠ und<br />

⎛<br />

⎝<br />

1 0 0<br />

0 1 0<br />

0 0 1<br />

⎞<br />

⎠:<br />

– lesen Sie die <strong>Matrizen</strong> m und e <strong>in</strong> e<strong>in</strong>em Programm e<strong>in</strong>, und geben Sie<br />

ihren Inhalt am Bildschirm aus;<br />

– multiplizieren Sie die beiden <strong>Matrizen</strong>; zeigen Sie, dass gilt m·e = e·m;<br />

– addieren Sie m und e.


16 KAPITEL 2. MATRIZEN IN <strong>C++</strong><br />

2.4 <strong>Matrizen</strong> <strong>in</strong> Fortran90<br />

Es gibt e<strong>in</strong>e Reihe anderer Programmiersprachen, <strong>in</strong> denen die wichtigsten Matrixoperationen<br />

bereits implemetiert s<strong>in</strong>d. Besonders e<strong>in</strong>fach ist das Rechnen mit <strong>Matrizen</strong> <strong>in</strong><br />

fortran90, auf das wir hier kurz e<strong>in</strong>gehen wollen.<br />

Beispielsweise werden <strong>in</strong> dem Programm<br />

1 program ma<strong>in</strong><br />

2<br />

3 real, dimension(0:2,0:2) :: a, b, c<br />

4<br />

5 a=0<br />

6 b=1<br />

7<br />

8 a(0,0)=1<br />

9<br />

10 c=matmul(a,b)<br />

11<br />

12 pr<strong>in</strong>t∗,c<br />

13<br />

14 end program<br />

<strong>in</strong> der Zeile 3 die <strong>Matrizen</strong> a,b,c der Größe 3 × 3 def<strong>in</strong>iert (Indizes von 0 bis 2), deren<br />

Matrixelemente <strong>in</strong> Zeilen 5 und 6 auf e<strong>in</strong>e Konstante gesetzt werden; <strong>in</strong> Zeile 8 wird<br />

das Element a00 gesetzt; <strong>in</strong> Zeile 10 erfolgt e<strong>in</strong>e Matrixmultiplikation von a und b; und<br />

schließlich wird die Matrix c <strong>in</strong> Zeile 12 ausgegeben.<br />

Wir sehen, dass all die Funktionen (und noch e<strong>in</strong>ige mehr), die wir <strong>in</strong> diesem <strong>Kapitel</strong><br />

programmiert haben, <strong>in</strong> fortran90 bereits vorhanden s<strong>in</strong>d. Dies ist auch e<strong>in</strong>er der<br />

Gründe, weshalb sich die Programmiersprache fortran <strong>in</strong> der Physik großer Beliebtheit<br />

erfreut. Dennoch wird sich zeigen, dass wir mit der selbst erstellten Matrixklasse matrix<br />

alle Probleme vollständig befriedigend lösen können.

Hurra! Ihre Datei wurde hochgeladen und ist bereit für die Veröffentlichung.

Erfolgreich gespeichert!

Leider ist etwas schief gelaufen!