OOP: Vektoren und Matrizen

Status
Nicht offen für weitere Antworten.

Illuvatar

Top Contributor
Huhu,

für ein privates Projektchen hab ich ein paar Klassen für Vektoren und (quadratische) Matrizen geschrieben. Ich weiß, dass es die schon gibt, aber mir geht es gerade darum, die selbst zu implementieren.
Allerdings bin ich mir bei der Umsetzung unsicher: vor allem damit, dass die Dinger verschiedene Dimensionen haben können, hab ich Probleme. Ich liste mal die Möglichkeiten auf, die mir bisher eingefallen sind (in der Hoffnung, es hat jemand eine bessere Idee ;)). Ich red jetzt mal nur von den Vektoren, aber das gleiche gilt für die Matrizen:

  1. Wie ich es bisher umgesetzt hab (und wie es anscheinend auch in javax.vecmath gemacht wurde): unterschiedliche Klassen für Vektoren mit unterschiedlichen Dimensionen Vector2, Vector3, Vector4. Keine Beziehungen der Klassen untereinander. Nachteil: Extrem viel redundanter Code.
  2. Meine erste Idee war: eine einzige Klasse, die ein Array der verschiedenen Koeffizienten hält. Aus der Arraylänge ergibt sich die Dimension. Nachteile: man müsste jede Methode der Klasse in etwa so beginnen:
    [HIGHLIGHT="Java"]public void add (Vector other) {
    if (other == null || other.getDimension() != this.getDimension()) {
    throw new IllegalArgumentException(...);
    }
    ...
    }[/HIGHLIGHT]
    Das finde ich irgendwie nicht schön (irgendwas in meinem Kopf schreit da außerdem was von "Performance", aber das versuche ich mal nicht zu beachten - vor allem weil die anderen Lösungen in der Hinsicht auch nicht besser sind). Insbesondere ein Kreuzprodukt würde schwierig. Solchen Code fände ich extrem seltsam:
    [HIGHLIGHT="Java"]public Vector cross (Vector other) {
    if (this.getDimension() != 3) {
    throw new IllegalStateException(...);
    if (other == null || other.getDimension() != 3) {
    throw new IllegalArgumentException(...);
    }
    ...
    }[/HIGHLIGHT]
  3. Kleine Verbesserung: Wie gerade eben, aber Unterklassen für die verschiedenen Dimensionen, oder zumindest für die dritte Dimension - dann könnte man das Kreuzprodukt nur in dieser Klasse implementieren. Die andere Problematik bleibt aber erhalten, außer man geht soweit dass man wieder bei Fall 1 ankommt.
  4. Vererbung: ein 2D-Vektor "ist ein" 3D-Vektor, in einem gewissen Sinne. Das führt aber dazu, dass z.B. Vector3 immernoch die Methode dotProduct(Vector2) erbt - die dann die ersten Komponenten einfach vernachlässigt. Auch sonst eigentlich irgendwie nicht viele Vorteile.
  5. Komposition: wäre zwar vertretbar, fände ich aber trotzdem komisch. Ein Vector4 besteht aus einem double und einem Vector3, der wiederum aus einem double und einem Vector2 besteht, der aus 2 doubles besteht ;) Das sähe dann irgendwie so aus:
    [HIGHLIGHT="Java"]public void add(Vector4 other)
    {
    if (other == null) {
    throw new IllegalArgumentException(...);
    }
    this.w += other.w;
    this.vec3.add (other.vec3);
    }
    public double dotProduct(Vector4 other)
    {
    if (other == null) {
    throw new IllegalArgumentException(...);
    }
    return this.vec3.dotProduct(other.vec3) + this.w * other.w;
    }
    [/HIGHLIGHT]
    Den Code eben könnte man vielleicht mit Generics sogar noch verallgemeinern, dass er auch für Vector3 gilt (und, angenommen ich mach noch eine Vector1-Klasse, dann könnte er auch für Vector2 gelten). Das macht es aber nicht gerade mehr intuitiv.
    Edit: Der Klassenname "VectorThatHas1DimensionMoreThan<T extends Vector>" würde mich allerdings durchaus reizen :D
 
Zuletzt bearbeitet:

0x7F800000

Top Contributor
Für Vector2D und Vector3D macht es durchaus sinn. Diese werden öfters in 2D und 3D spielchen gebraucht, also für rein anschauliche Geometrische anwendungen. Redundanter code: ja. Aber es macht nur bei diesen 2 Klassen sinn, daher ist das nicht so schlimm, der redundante code ist nur auf diese zwei klassen beschränkt.

"Kreuzprodukt" macht eh nur bei 3D sinn, und ist außer in der geometrie überall völlig nutzlos. Oder wenn schon verallgemeinern: du kannst in jeder dimension aus (n-1) verschiedenen n-dimensionalen vektoren einen dazu orthogonalen vektor konstruieren, man denke da an definition des Kreuzproduktes als formale Determinante mit einheitsvektoren in einer spalte oder zeile. Also, ich würde sagen: in einer allgemeinen Double-Vektor Klasse macht Kreuzprodukt keinen sinn, braucht kein Mensch.

Was nicht passende dimensionen bei einer allgemeinen VectorXD-Klasse angeht: ich löse das bisher gaaanz einfach:
Ich nehme einfach an, dass alle dimensionen Stimmen, und rechne los.
Wenn irgendwas nicht passt, endet alles mit einer ArrayIndexOutOfBoundsException.
Warum nicht? Alle sind dann glücklich:
-Ich hab's einfach
-Performance leidet nicht
-Wenn eine exception fliegt, weiß der Benutzer, dass er Mist gebaut hat.
Also halte ich das für die beste Lösung, und bleibe erstmal dabei.

Und wenn man gaaanz fleißig ist, kann man ja nochmal für debug zwecke so eine Paranoide Matrixklasse bauen, die alle dimensionen checkt.

Ach ja, übrigens: zwischen Vektoren und matrizen sollte man imho nicht unterscheiden, ein vector ist dann einfach eine 1-Spaltige matrix, fertig.
Und ja, nochwas: im allgemeinen Fall braucht man sich nicht auf rechteckige matrizen einzuschränken, dadurch gewinnt man ziemlich genau nichts.
 
Zuletzt bearbeitet von einem Moderator:

didjitalist

Bekanntes Mitglied
bin auch der meinung, dass man bei solch basalen klassen auf parameterprüfung verzichten kann. du gewinnst dadurch wenig, im grunde nur eine schönere fehlermeldung. wenn die dimensionen nicht stimmen, fliegt eh extrem schnell eine ArrayIndexOutOfBoundsException, die für den aufrufer sehr leicht verständlich ist. zumal der code ohne fehlerbehandlung extrem kurz und übersichtlich ist.
 

Marco13

Top Contributor
Ich verwende sher oft die vecmath. Dort sind es unterschiedliche Klassen. (Und auch einer von wenigen Fällen, wo gegen das "oberste Prinzip", keine public Members anzubieten, verstoßen wurde: vector3f.x, y, und z sind public floats - vielleicht (Altlasten) aus Performancegründen, vielleicht auch weil es egal ist.......)

Du meintest, der Nachteil daran wäre der redundante Code. Es entsteht zwar redundanter Code, aber IMHO nicht sooo viel. (Störender empfinde ich teilweise wirklich die tatsache, dass man einen Vector3f nicht "mal kurz" als Vector2f verwenden kann, aber das ist doch auch zu verschmerzen). Was ich nur andeuten wollte: Wenn man sich die "Einmaligkeit" (d.h. nicht-Redundanz) solcher Trivialmethoden wie "length" oder "getZ" damit erkauft, dass jede dieser Methoden wegen irgendwelcher Bounds-Checks, Generics und Arraygewurschtel 3mal so langsam, 4mal so lang und 5mal so kompliziert ist, wie die redundante Version, sollte man sehr genau abwägen, was einem wichtiger ist...

Ich fände eine "unifizierte" Behandlung der unterschiedlichen Dimensionalitäten auch schöner - aber das ist wieder einmal einer der Fälle, wo man sich zwar etwas theoretisch total tolles ausdenken könnte, was sehr nah an dem sein könnte, was ein Mathematiker alles mit solchen Dingen veranstalten kann, was aber aufgrund sprachlicher Einschränkungen nicht unbedingt auch "schön" umzusetzen ist...
 

slawaweis

Bekanntes Mitglied
@Illuvatar
meine Erfahrung mit solchen Sachen ist, dass wenn man eine Art Framework programmiert, es oft sinnvoll ist die selbe Sache auf mehrere Arten zu implementieren. Wenn man von Anfang an nicht genau weis, was man später braucht, so ist die beste Lösung jede Variante umzusetzen. Weil wenn man später merkt, dass man die falsche Variante gewählt hat, quält man sich entweder damit oder muss dann die andere Variante nachreichen. Also wäre es sinnvoll das Ganze mit mehreren Klassen und mit einer universellen Klasse umzusetzen und später zu entscheiden, was man nimmt.

Was die Redundanz bei mehreren Klassen angeht, das läst sich mit einem eigenen Präprozessor lösen. Wenn man viele ähnliche Klassen oder Funktionen erstellen will, dann erstellt man sich ein Programm, das diese Klassen generiert. Also eine Art Builder für Quelltext. Dort gibt es z.B. eine Funktion "generateVectorClass(int dimension, File target)" und diese erstellt dann die Datei "VectorX.java", von 1D bis nD. Will man dann irgendwas an den VectorX Klassen ändern, ändert man den Builder und generiert die Klassen neu.

Noch eine Sache zu den Vector-Klassen. Wenn man irgendwann in einem Programm mit 10.000 oder 100.000 Vektoren hantiert, ist für jeden Vektor eine Klasse zu erstellen sehr ineffizient. Falls also irgendwann viele Vektoren verarbeitet werden müssten, empfehle ich schon vorab eine "VectorArray" Klasse anzulegen, die beliebig viele Vektoren in einem Feld verwaltet.

Slawa
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben