un vettore2 estende un vettore 3 o è il contrario?

1

Forse la domanda potrebbe essere legata a un forum teorico o matematico, ma dal momento che è per scopi di programmazione, chiedo qui prima:

In un contesto di visione del computer, scrivo un paio di interfacce intese per essere la parte "di sola lettura" dei vettori. Così definisco "IVector2R" e "IVector3R" che contengono solo getter. La domanda è: fa IVector2R estende IVector3R (e "y getter" restituisce sempre 0), o è il contrario: IVector3R estende IVector2R?

Vorrei una concezione il più vicino possibile alla teoria matematica / degli insiemi ...

Grazie per l'attenzione

    
posta Clem 28.11.2013 - 18:59
fonte

5 risposte

5

I would like a conception as close as possible to the mathematic/sets theory…

Quando lavori con le coordinate vettoriali, significa che lavori in uno spazio vettoriale in cui hai scelto una base. Chiedere se esiste una relazione naturale tra vettori bidimensionali e vettori tridimensionali è la stessa cosa che chiedere se esiste una relazione naturale tra uno spazio vettoriale bidimensionale dotato di una base e uno spazio vettoriale tridimensionale dotato di una base. In generale, non c'è.

Tuttavia, sembra che tu stia lavorando con uno spazio euclideo con una base (e1, e2, e3) in modo che il vettore di coordinate (X, Y, Z) sia Xe1 + Ye2 + Ze3. Se si assume che lo spazio bidimensionale abbia la base (e1, e2), allora ci sono due mappe naturali tra vettori bidimensionali e vettori tridimensionali:

- The projection (X,Y,Z) -> (X,Y)
- The embedding (X,Y) -> (X,Y,0)

Pertanto, da un punto di vista matematico, non esiste un modo naturale per esprimere una relazione tra vettori bidimensionali e vettori tridimensionali che potresti esprimere attraverso l'ereditarietà. Esistono tuttavia due trasformazioni naturali che è possibile implementare come funzioni regolari.

    
risposta data 18.12.2013 - 13:30
fonte
3

A mio parere, né né: Entrambi sono n -le tuple con differenti n . Il problema è che la maggior parte dei linguaggi non consente di parametrizzare i tipi con un valore.

Quando decidi una certa ereditarietà, dovresti considerare la moltiplicazione della matrice.

/1 0\   /2\ ?  /1 0 0\   /2\
// failure with Vector3 <: Vector2 -- vec.z can be != 0
if (vec instanceof Vector2)
  assert vec.z == 0;

// failure with Vector3 <: Vector2 -- vec.z can be != 0
if (vec instanceof Vector2)
  assert vec.norm() == sqrt(vec.x^2 + vec.y^2); // euclidean norm - invalid in non-cartesian systems

// failure with Vector2 <: Vector3 if they are mutable (Circle-Ellipse Problem)
if (vec instanceof Vector2) {
  vec.z = 42;
  assert vec.z == 0;
}

// failures concerning dimensionality with *any* inheritance relation
// when considering matrix multiplication
1/ · |3| = |0 1 0| · / /
/1 0\   /2\ ?  /1 0 0\   /2\
// failure with Vector3 <: Vector2 -- vec.z can be != 0
if (vec instanceof Vector2)
  assert vec.z == 0;

// failure with Vector3 <: Vector2 -- vec.z can be != 0
if (vec instanceof Vector2)
  assert vec.norm() == sqrt(vec.x^2 + vec.y^2); // euclidean norm - invalid in non-cartesian systems

// failure with Vector2 <: Vector3 if they are mutable (Circle-Ellipse Problem)
if (vec instanceof Vector2) {
  vec.z = 42;
  assert vec.z == 0;
}

// failures concerning dimensionality with *any* inheritance relation
// when considering matrix multiplication
1/ · |3| = |0 1 0| · / / %pre% 0 0/
0 0/

Stai implicitamente sostenendo che una o entrambe le moltiplicazioni funzionano (e dovrebbero eventualmente essere equivalenti). A questo punto ogni matematico sta avendo un attacco di cuore, perché ha violato alcune regole sulla dimensionalità richiesta.

Penso che sia ovvio che Vector3 non può essere un sottotipo di Vector2 , e non il contrario: non puoi generalmente usarne uno al posto dell'altro. Entrambi hanno proprietà comuni . Definirei probabilmente un'interfaccia Vector con metodi come component(i) che fornisce il componente i -th (invece di getX , getY , ...), size() che fornisce la dimensionalità e norm() che calcola la lunghezza.

Se sei deciso ad avere un tipo che eredita da un altro, considera questi casi di test:

%pre%

Ciò significa che devo ammettere a malincuore che avere Vector2 ereditato da Vector3 potrebbe funzionare nella maggior parte dei casi, se sono immutabili e non stai facendo nulla di più di un'aggiunta o una moltiplicazione scalare.

Nota sulle coordinate

Se non vuoi fare algebra vettoriale ma vuoi semplicemente rappresentare le coordinate, allora questa risposta sarebbe diversa, perché le coordinate 2D possono essere viste come una proiezione di coordinate 3D in un piano (o qualsiasi altra superficie parametrizzabile da due numeri). In questo caso, ogni coordinata 2D avrà anche una coordinata 3D, ma le due coordinate provengono da due diversi sistemi di coordinate. La proiezione su z = 0 -plane è un caso molto particolare di ciò in cui è possibile visualizzare la coordinata 2D come una sorta di coordinata 3D. Questo non è generalmente il caso in tutti i sistemi di coordinate 2D.

    
risposta data 28.11.2013 - 19:40
fonte
2

Interessante.

Dal Principio di sostituzione di Liskov , si potrebbe obiettare che un Vector3R "è un" Vector2R, ma solo con più cose (un asse Z se queste sono dimensioni, un "g get" nel tuo esempio di visione). Penso che da un "purista" matematica / set POV questo funziona meglio. Lo svantaggio è che la gerarchia delle classi potrebbe diventare caotica, con eventuali Vector4R, SomeSpecialVector3R, ImmutableVector3R, ecc ...

Quindi, sostenendo che un Vector2R è un Vector3R ma restituisce sempre 0 (o null, ecc.) ha anche un po 'di senso. E potrebbe rendere la tua gerarchia di classe molto meno disordinata. Ad esempio, non hai bisogno di una classe Vector2R. Forse solo un metodo aggiuntivo, whatIsMyN() .

L'opzione # 2 è il modo in cui Java gestisce le raccolte - invece di una classe di gazillion in una gerarchia densa, è permesso dire "no" restituendo 0 (in realtà, nel loro caso, fanno ancora peggio, lanciano un'eccezione) . Ad alcuni programmatori piace, e alcuni lo odiano. Svantaggio: richiede che il tuo codice sia un po 'più intelligente: o chiedi in anticipo se questo "Vector3" ha un getter y, o essere preparato e pronto a gestire il ritorno degli 0.

Ho codificato in entrambi i modi. Dipende da quanto "ragionevole" sia, nel tuo dominio, che lo 0 ritorni.

    
risposta data 28.11.2013 - 19:23
fonte
0

Si prega di definire quali metodi o operazioni hanno in comune Vector2R e Vector3R e il caso d'uso in cui si vorrebbe trattarli allo stesso modo, senza bisogno di sapere quale sia. Queste sarebbero le ragioni per trovare un'interfaccia comune. In caso contrario, non c'è una risposta utile alla tua domanda.

Suppongo che la moltiplicazione della matrice sia il tuo caso d'uso? "Vector" e "Array" sono termini caricati in alcune lingue. In Java sono uguali, ma in Scala Vector = albero poco profondo. In JavaScript, Array = Mappa / Dizionario. Anche in matematica e fisica, "Vector" può significare molte cose .

Altrimenti, "Favorisci la composizione sull'ereditarietà". Ma ti darò alcune informazioni di base.

La maggior parte delle lingue ha matrici multidimensionali integrate:

La moltiplicazione della matrice può essere eseguita anche nella maggior parte delle lingue:

Quindi la maggior parte delle lingue affronta i tuoi problemi o non fornisce supporto per l'astrazione di n dimensioni dove n può cambiare. Questo è il motivo per cui il tuo caso d'uso è fondamentale per progettare un'astrazione significativa per il trattamento di matrici di dimensioni diverse uguali.

    
risposta data 18.12.2013 - 14:33
fonte
0

Quindi, alla fine, semplifico dichiarando solo un'interfaccia IMatrixR . Ho lasciato che l'implementazione facesse cose più "ottimizzate" con classi specifiche come Vector3 o Vector4, entrambe implementando l'interfaccia IMatrixR.

Ma a livello generico, il diagramma UML diventa un po 'più complicato da disegnare poiché ci sono più vincoli da indicare e un insieme di eccezioni (o codice di errore da restituire) da specificare su ciascun metodo; quelle che implicano condizioni che preferirei evitare automaticamente dal processo di controllo del tipo durante la compilazione.

Ad esempio, il metodo mul(IMatrixR,IMatrixR) deve sollevare un'eccezione quando le dimensioni non sono compatibili. Ma il metodo di overload mul(Matrix4,Vector4) non lo butterà mai! Mentre l'implementazione standard di mul(IMatrixR,IMatrixR) lo fa. Il metodo di sovraccarico sarà più efficiente e non richiede di essere circondato da try / catch ...

    
risposta data 31.01.2014 - 16:27
fonte

Leggi altre domande sui tag