verbesserungsvorschläge für endlich fertig gestellte klasse
-
#ifndef matrix_klasse #define matrix_klassediese bezeichner sollen was sein, wo du sicher bist, daß du sie nicht woanders aus versehen verwendest. deswegen nehme ich MATRIX_H. und ich nehme bezeichner mit _H am ende nur für include-guards. soll nicht heßen, daß du auch _H nehmen sollst. sollst nur schauen, ob mit deinem restlichen stil sicher ist, daß du matrix_klasse nicht woanders verwendest.
#include <iostream> #include <cstdlib>fein.
using namespace std;nicht fein. mach nie im haeder using namespace. was ist, wenn du mal ne andere lib mit nem anderen cout verwenden möchtest? oder nur ein anderes swap? durch dein using namespace erzwingst du, daß jeder benutzer deiner matrix-klasse auch using namespace std; gemacht hat, auch wenn er eientlich lieber using namespace vhlib; machen würde und beide nicht nebeneinander gehen.
template <class T> class matrix {naja, ich mag grossbuchstaben.
public:bitte zeig zuerst den private-teil mit den attributen. so kann ein fremder leser viel schneller raffen, was die klasse ist und was sie macht.
matrix(unsigned int z); //eine dimension matrix(unsigned int z, unsigned int s); //zwei dimemsionendas ist schlimm. mach matrix und matrix2d und matrix3d als getrennte klassen. damit kriegste mehr performance und mehr zurgiffssicherheit.
//matrix(int, int, int); //noch nicht ~matrix();fein
void ausgabe_matrix();lieber void print_at(ostream& out);, damit man auch mal in ne datei schreiben kann, wenn einem lustig danach ist.
void setze_in_matrix(T *feld, unsigned int zeilen_matrix); //schiebt hinten rein(matrix) void setze_in_matrix(T *feld, unsigned int zeilen_matrix, int pos_z); //für eindim array void setze_in_matrix(T *feld, unsigned int zeilen_matrix, unsigned int spalten_matrix, int pos_z, int pos_s); //für zweidim matrix void fuege_array_ein(T *array);vielleicht isses netter (nee, sicher, nicht nur vielleicht), wenn du den op[] bzw op[][] bzw op[][][] überlädst?
private: T *matrix_eindim; T **matrix_zweidim; //T ***matrix_dreidim;gleichzeitig???
unsigned int zeilen_matr; unsigned int spalten_matr; unsigned int index_z_eindim; //damit verwechlungen ausgeschlossen sind unsigned int index_z_zweidim; unsigned int index_s_zweidim;ich sag's doch. mehrere klassen, dann verwechslt sich auch nix.
}; //##############################################################################fein.
template <class T> matrix<T>::matrix(unsigned int zeilen) { index_z_eindim = 0; matrix_eindim = new T[zeilen]; zeilen_matr = zeilen; }och, was triviales kannste doch auch inline in der klasse anbieten.
template <class T> matrix<T>::matrix(unsigned int zeilen, unsigned int spalten) { index_z_zweidim = 0; index_s_zweidim = 0; matrix_zweidim = new T*[zeilen]; for(int i=0; i < zeilen; i++) matrix_zweidim[i] = new T[spalten]; zeilen_matr = zeilen; spalten_matr = spalten; }wieder verwirrend, weil die klasse irgendwie zwei aufgaben hat.
ach, was sage ich, die hat 6 aufgaben.also mach erstmal aus den drei matrix-aufgaben auch drei klassen. dann trenne die speicher-haltungs-klasse von der matrix-zugriffs-klasse.
rest brauche ich nicht mehr zu lesen, hast erstmal ein paar vorschläge.
-
delete mit einer Schleife
Und noch ein paar Designaspekte: Deine Ewig_Lange_Elementfunktionnamen_die_ueberhaupt_nichts_aussagen_sind_auch_nicht_gut
Überlege Dir kurze und prägnante Namen:
Beispiel:
Matrix<int> my_matrix(...) ... my_matrix.setze_in_zeile(...) my_matrix.ausgabe_matrix()hört sich nicht so toll an
wie wäre es mit
Matrix<int> my_matrix(...) ... my_matrix.redefineRow(...) my_matrix.print()Ausserdem ist es aus Performancegründen sicher sinnvoll gleich ganze Blöcke zu kopieren statt alles elementweise.
Mir fällt noch eine ganze Menge mehr ein, was ich verbessern würde. Aber darüber können wir gerne morgen diskutieren, bin jetzt platt.
PS: Was ist das für ein Buch?
Gruß
turing
-
print_at(ostream& out);
warum kein operator<<?
-
danke ich werde deine ratschläge bis morgen umsetzen und dann die klasse/n posten.
nur eins noch, ich weiß mit der ausgabefunktion nicht so recht viel anzufangen
wie müsste das hier richtig aussehen
:void matrix<T>::print_at(std::ostream& ausgabe); { //if(spalten_matr < 1) for(unsigned int i = 0; i < zeilen_matr; i++) (std::cout.ausgabe<<("Position ")).ausgabe<<(i); (std::cout.ausgabe<<(": ").ausgabe<<(matrix_eindim[i]); (std::cout.ausgabe<<(std::endl) ); /* else { for(unsigned int i = 0; i < zeilen_matr; i++) { cout<<endl; for(unsigned int j = 0; j < spalten_matr; j++) cout<< "Position " << i << ": " << matrix_zweidim[i][j]; } } */ return ausgabe; }
-
strings schrieb:
print_at(ostream& out);
warum kein operator<<?
ach, den hab ich doch glatt vergessen.
ostream& operator<<(ostream& out,Matrix const& m){ return m.printAt(out); }und weil ich irgendwo sowas wie
template<typename T> ostream& operator<<(ostream& out,T const& t){ return t.printAt(out); }stehen habe, reicht's bei mir immer, nur eine printAt anzubieten, wenn ich eine klasse ausgebbar machen mag.
-
warum denn dann 2 methoden für die ausgabe??
-
strings schrieb:
warum denn dann 2 methoden für die ausgabe??
die printAt()-Methode brauchst du wenn du ein objekt polymorph ausgeben willst, virtual funktioniert leider bei normalen Funktionsaufrufen nicht.
der template-wrapper ist ne gute idee

-
Volkard du hast ja echt VIEL Zeit
Und bitte_bitte_bitte verwende doch Englisch im Code wirkt viel professioneller und ist mit der Zeit besser und auch von allen in der gesamten Welt besser zu lesen.
-
T *matrix_eindim; T **matrix_zweidim;Und warum machst du in einer Matrixklasse ein und zweidimensionale Matrizen. Normalerweise hast du eine Klasse Matrix2d Matrix3d Matrix4d... Was ist auch bitte eine 1dimensionale Matrix? kannst ja gleich nen float nehmen?
-
dali schrieb:
Was ist auch bitte eine 1dimensionale Matrix? kannst ja gleich nen float nehmen?
nee. ein float ist 0d.
-
[quote="dali"]Volkard du hast ja echt VIEL Zeit ;)[quote]
ich habe VIEL zu wenig zeit.Und bitte_bitte_bitte verwende doch Englisch im Code
das mache ich manchmal. mit welchem code haste im moment ein problem?
wirkt viel professioneller
ich lege nicht extrem viel wert darauf, durch irgendwas professioneller zu wirken, als ich bin. besonders im code nicht. den mache ich so gut und so lesbar unter nichtvernachlässigung von ein wenig erweiterbarkeit und performance, wie ich kann. im allgemeinen mache ich projektübergreifend wiederverwendbares auf englisch und domain-spezifisches auf deutsch. das ergibt auch eine schöne und augenfällige schichtentrenung. einfach köstlich.
und ist mit der Zeit besser und auch von allen in der gesamten Welt besser zu lesen.
da ich eh keine kommentare mache, ist das problem schon fast aus der welt. *g*
-
Das mit dem Englisch war eigentlich auf exigoner bezogen. Ist sicher Geschmackssache was man verwendet aber Englisch und Deutsch mixen find ich nicht gut... Und Code ohne Kommentare naja... Mir hat man immer Punkte bei den Übungen abgezogen

-
So noch ein bisschen (?) Matrixdesign.
Was willst Du eigentlich mit der Matrix machen? Ich kenne Matrizen in erster Linie aus der diskreten Mathematik oder Numerik.
Für einen Datentyp Matrix (der in dem o.g. Kontext eingesetzt werden soll) bietet sich an:
1.) Betreibe zuerst Matrixdesign
Das Wichtigste. Überlege Dir zuerst ganz genau, was die Matrix können soll und "make the common case fast".
2.) Implementiere nur den Datentyp Matrix.
Es gibt keine "eindimensionale" oder "zweidimensionale" Matrix, in dem Sinne wie es hier verwendet wird. Eine Matrix besteht immer (!) aus Zeilen und Spalten.
Und deswegen gibt es nur "zweidimensionale" Matrizen.
Bsp:
[1 0] ist eine (1x2)-Matrix
[1 0; ist eine (2x2)-Matrix
[0 1]
[1] ist eine (1x1)-MatrixDie Dimension einer Matrix ist übrigens die Anzahl der linear unabhängigen Zeilen bzw. Spalten.
3.) Repräsentiere den Inhalt der Matrix als doppelt verkettete Liste.
Das mag vielleicht auf den ersten Blick wie Speicherverschwendung aussehen, ist aber tatsächlich, was übliche Matrixoperationen angeht, wesentlich effizienter. Du kannst Dir ja als Übung überlegen, warum.
Hier nur ein paar Hinweise:
- Hinzufügen von Zeilen/Spalten wird einfacher
- Löschen von Zeilen/Spalten wird einfacher
- Tauschen von Zeilen/Spalten wird einfacher
- Transponieren wird einfacher
- Matrix-Matrix Multiplikation wird einfacher
- Gaussalgorithmus wird einfacher
- Konsistenes Prüfen auf NULL-Zeiger4.) Wähle eine einfach Schnittstelle
Neben Kontruktor/Destruktor muss die Matrix eigentlich nur +, -, * (und /) sowie Zeilen/Spalten Manipulationen beherrschen. Weitere Elementfunktionen würde ich nur aufnehmen wenn Du sie wirklich häufig brauchst, ansonsten auslagern.
5.) Programmiere einfach und effizient
Auch sehr wichtig. Ständiges Hin- und Herkopieren von irgendwelchen Werten ist ziemlich laaaaaaaangsam (stattdessen ein paar Zeiger verbiegen). Ebenso ständiges new und delete (lieber Puffer und Speicherpools verwenden). Überprüfe immer wieder, ob es nicht auch einfacher geht.
So genug Lehrmeisterei. Das hat vielleicht sogar ein bisschen zu weit geführt, sollte Dir aber auch zeigen, dass selbst so etwas einfach Erscheinendes wie eine Matrix verdammt schwer zu implementieren ist. Aber es reicht ja schon, wenn Du einfach ein paar Ideen bekommen hast, wie Du Deine Matrixklasse verbessern
kannst.Wenn jemand Spass daran hat und ein wenig Langeweile, kann sie/er ja mal versuchen eine effiziente Matrixklasse zu implementieren, die die üblichen Operationen darauf unterstützt und dann zum Beispiel an Bitmaps austesten.
Schonmal versucht, alle Eigenvektoren einer Bitmap zu bestimmen? Oder gar zu invertieren?Gruss
turing
-
die ganzen ratschläge lassen sich ja gut umsetzen, aber ich verstehe die ausgabefunktion einfach nicht.
packe ich das in meine klassestd::ostream& operator<<(std::ostream& out, matrix const& m) return m.printAt(out);beomme ich fehler
-
Turing schrieb:
3.) Repräsentiere den Inhalt der Matrix als doppelt verkettete Liste.
Das mag vielleicht auf den ersten Blick wie Speicherverschwendung aussehen, ist aber tatsächlich, was übliche Matrixoperationen angeht, wesentlich effizienter. Du kannst Dir ja als Übung überlegen, warum.
Hier nur ein paar Hinweise:
[...]
- Matrix-Matrix Multiplikation wird einfacher
- Gaussalgorithmus wird einfacherBegründung?
Was dagegen spricht:
- Speicherverbrauch wird bei dichtbesetzten Matrizen viel höher
- Index-Zugriffe werden langsamerNene, verkettet bitte nur bei großen dünnbesetzten Matrizen.
Im Idealfall hat man mehrere Matrixklassen in der Schublade aus denen man je nach Anwendungsfall wählen kann:
- Eine dynamische wenn die Dimensionen erst zur Laufzeit bekannt sind.
- Eine generische, wenn die Dimensionen zur Compilezeit feststehen.
- Mehrere für dünnbesetzte Matrizen die verkettet, mit einer Hashtable oder ganz anders implementiert sind, je nach Aufbau der Matrix.
- ...
-
ich werd erstmal keine verketteten listen benutzen um ne basisklasse zu schreiben, vielleicht später zum ausprobieren, aber niccht jetzt.
-
so was haltet ihr davon?, abgesehen von der ausgabe

#ifndef MATRIX_KLASSE #define MATRIX_KLASSE #include <iostream> #include <cstdlib> template <class T> class matrix { private: T **matrix_zweidim; unsigned int zeilen_array; unsigned int spalten_array; unsigned int index_z; unsigned int index_s; public: matrix(unsigned int z, unsigned int s); //zwei dimemsionen ~matrix(); T *get_matrix(); void ausgabe_array(); void push_pos(T *feld, unsigned int zeilen_array, unsigned int spalten_array, int pos_z, int pos_s); //für zweidim matrix }; //############################################################################## template <class T> inline matrix<T>::matrix(unsigned int zeilen, unsigned int spalten) : index_z(0), index_s(0), zeilen_array(zeilen), spalten_array(spalten) { matrix_zweidim = new T*[zeilen]; for(int i=0; i < zeilen; i++) matrix_zweidim[i] = new T[spalten]; } template <class T> matrix<T>::~matrix() { for(int i=0; i < zeilen_array; i++) delete [] matrix_zweidim[i]; delete [] matrix_zweidim; } template <class T> T *matrix<T>::get_matrix() { return matrix_zweidim; } template <class T> void matrix<T>::push_pos(T *feld, unsigned int feld_zeilen, unsigned int feld_spalten, int position_zeile, int position_spalte) { for(unsigned int i = position_zeile, j = 0; i < (feld_zeilen + position_zeile); i++, j++) for(unsigned int k = position_zeile, l = 0; k < (feld_spalten + position_spalte); k++, l++) matrix_zweidim[i][k] = feld[j][l]; if( (feld_zeilen + position_zeile) > index_z) index_z = (feld_zeilen + position_zeile); if( (feld_spalten + position_spalte) > index_s) index_s = (feld_spalten + position_spalte); } #endif#ifndef ARRAY_KLASSE #define ARRAY_KLASSE #include <iostream> #include <cstdlib> template <class T> class array { private: T *array_eindim; unsigned int zeilen_array; unsigned int index_z; //damit verwechlungen ausgeschlossen sind public: array(unsigned int z); //eine dimension ~array(); T *get_array(); void ausgabe_array(); //std::ostream& operator<<(std::ostream& out, matrix const& m) return m.printAt(out); void push_back(T *feld, unsigned int zeilen_array); //schiebt hinten rein(matrix) void push_pos(T *feld, unsigned int zeilen_array, int pos_z); //für eindim array }; //############################################################################## template <class T> inline array<T>::array(unsigned int zeilen) : index_z(0), zeilen_array(zeilen) { array_eindim = new T[zeilen]; } template <class T> array<T>::~array() { delete [] array_eindim; } template <class T> T *array<T>::get_array() { return array_eindim; } template <class T> void array<T>::ausgabe_array() { for(unsigned int i = 0; i < zeilen_array; i++) std::cout<< "Position " << i << ": " << array_eindim[i] << std::endl; } template <class T> void array<T>::push_back(T *feld, unsigned int feld_zeilen) { for(unsigned int i = 0; i < feld_zeilen; ++i && ++index_z) array_eindim[index_z] = feld[i]; } template <class T> void array<T>::push_pos(T *feld, unsigned int feld_zeilen, int position_zeile) { for(unsigned int i = position_zeile, j = 0; i < (feld_zeilen + position_zeile); i++, j++) array_eindim[i] = feld[j]; if( (feld_zeilen + position_zeile) > index_z) index_z = (feld_zeilen + position_zeile); } #endifich wollts noch machen das man beim erstellen der matrix/arrays die größe beim objekt NICHT angeben brauchsondern angefangen bei einer zeile automatisch erhöht. wie kann ich das machen? kann man da einfach immer wieder "new []" aufrufen? oder muss ich da ein neuen array tmp[] erstellen?
-
Es ehrt Dich, dass Du BLAS kennst und mich darauf hinweist. Aber mein Vorschlag mit den verketteteten Listen hatte nicht den Anspruch damit zu konkurrieren.
Und sicherlich ist die Lösung mit den doppelt verketteten Listen auch nicht die beste und schnellste.
Was aber zum Beispiel den Aufwand oder den Schwierigkeitsgrad angeht ist es meiner Meinung ein guter Kompromiss zwischen Einfachheit und Effizienz für übliche Matrixoperationen. Das kommt aber auch wieder darauf an, ob ich gerne mit Zeigern oder Indices rechne. Was einfach ist und was nicht, hängt wahrscheinlich auch sehr vom eigenem Geschmack ab.Und viel höher ist der Speicherverbrauch auch nicht. So etwa das Dreifache...
Und der Gewinn? Naja, wie schon gesagt, es wird einiges einfacher.
Okay, bei einer (1000x1000)-Matrix mit 32 Bit Einträgen wären das statt 4 MB eben 12 MB, noch erträglich finde ich. Aber spätestens bei (1000x1000)-Matrizen sollte dann auch die Grenze dessen liegen, was man "mal eben" von Hand programmieren will.Viel Spass noch beim Weiterdiskutieren.
Gruss
turing
-
ich wollte meine arrays für ein neuronales netz benutzen, ich habe noch nie verkettete listen benutzt, wären die für solche arten der anwendung effizient?
-
dali schrieb:
Und Code ohne Kommentare naja... Mir hat man immer Punkte bei den Übungen abgezogen

Ja, aber das liegt einfach daran, dass Tutoren notorisch nicht programmieren können. Die allermeisten Kommentare die ich in Codes lese (ob nun an der Uni, im Netz oder in sonstigen Projekten), sind vollkommen sinnfrei. Wenn man ordentlichen Code schreibt, reduziert sich der zu kommentierende Teil auf ein Minimum. In meinem Fall wäre das fast ausschließlich das Kommentieren von Funktionen und Klassen im Dokumentationsstil (JavaDoc, XML, ...).