Quadtree con duplicati


10

Sto implementando un quadtree. Per coloro che non conoscono questa struttura di dati, includo la seguente piccola descrizione:

Un Quadtree è una struttura di dati ed è sul piano euclideo ciò che un Octree è in uno spazio tridimensionale. Un uso comune dei quadrifici è l'indicizzazione spaziale.

Per riassumere come funzionano, un quadtree è una raccolta - diciamo dei rettangoli qui - con una capacità massima e un riquadro di delimitazione iniziale. Quando si tenta di inserire un elemento in un quadrifoglio che ha raggiunto la sua capacità massima, il quadrifoglio viene suddiviso in 4 quadrifogli (una rappresentazione geometrica della quale avrà un'area quattro volte più piccola rispetto all'albero prima dell'inserimento); ogni elemento viene ridistribuito nei sottotitoli in base alla sua posizione, ad es. il limite superiore sinistro quando si lavora con i rettangoli.

Quindi un quadrifoglio è o una foglia e ha meno elementi della sua capacità, o un albero con 4 quadrifogli da bambini (di solito nord-ovest, nord-est, sud-ovest, sud-est).

La mia preoccupazione è che se si tenta di aggiungere duplicati, può essere lo stesso elemento più volte o più elementi diversi con la stessa posizione, i quadricipiti hanno un problema fondamentale con la gestione dei bordi.

Ad esempio, se lavori con un quadrifoglio con una capacità di 1 e il rettangolo dell'unità come rettangolo di selezione:

[(0,0),(0,1),(1,1),(1,0)]

E provi a inserire due volte un rettangolo il cui limite superiore sinistro è l'origine: (o similmente se provi a inserirlo N + 1 volte in un quadrifoglio con una capacità di N> 1)

quadtree->insert(0.0, 0.0, 0.1, 0.1)
quadtree->insert(0.0, 0.0, 0.1, 0.1)

Il primo inserto non sarà un problema: Primo inserto

Ma poi il primo inserimento attiverà una suddivisione (perché la capacità è 1): Secondo inserto, prima suddivisione

Entrambi i rettangoli vengono quindi inseriti nella stessa sottostruttura.

Quindi di nuovo, i due elementi arriveranno nello stesso quadrifoglio e scateneranno una suddivisione ... Secondo inserto, seconda suddivisione

E così via, e così via, il metodo di suddivisione verrà eseguito indefinitamente perché (0, 0) sarà sempre nella stessa sottostruttura dei quattro creati, il che significa che si verifica un problema di ricorsione infinita.

È possibile avere un quadrifoglio con duplicati? (In caso contrario, si può implementarlo come a Set)

Come possiamo risolvere questo problema senza rompere completamente l'architettura di un quadrifoglio?


Come vorresti che si comportasse? Lo stai implementando, quindi devi decidere quale comportamento è corretto per te. Forse ogni coordinata univoca può essere un elenco di elementi in corrispondenza di quella coordinata. Forse i tuoi punti sono vincolati ad essere unici. Sai di cosa hai bisogno e noi no.
Inutile

@Useless È vero. Tuttavia, ci sono state molte ricerche sull'argomento e non voglio nemmeno reinventare la ruota. TBH Non so ancora se questa domanda appartiene più a SO, a programmers.SE, a gamedev.SE o anche a math.SE ...
Pierre Arlaud,

Risposte:


3

Stai implementando una struttura di dati, quindi devi prendere decisioni di implementazione.

A meno che il quadtree non abbia qualcosa di specifico da dire sull'unicità - e non ne sono consapevole - questa è una decisione di implementazione. È ortogonale alla definizione di quadrifoglio e puoi scegliere di gestirlo come preferisci. Quadtree spiega come inserire e aggiornare le chiavi, ma non se devono essere univoci o cosa è possibile collegare a ciascun nodo.

Prendere decisioni di implementazione non sta reinventando la ruota , almeno non più che scrivere la propria implementazione in primo luogo.

Per fare un confronto, la libreria standard C ++ offre un set unico, un multiset non univoco, una mappa univoca (essenzialmente un set di coppie chiave-valore ordinate e confrontate solo dalla chiave) e una multimappa non unica. Sono generalmente implementati utilizzando lo stesso albero rosso-nero e nessuno rompe l'architettura , semplicemente perché la definizione dell'albero rosso-nero non ha nulla da dire sull'unicità delle chiavi o dei tipi memorizzati nei nodi foglia.

Infine, se pensi che ci siano ricerche su questo, trovalo e quindi possiamo discuterne. Forse c'è qualche invariante quadrifoglio che ho trascurato, o qualche vincolo aggiuntivo che consente prestazioni migliori.


Il mio problema è che non riesco a trovare alcuna documentazione attestante che l'unicità è un requisito. Tuttavia, se hai visto il mio esempio, puoi vedere che è un vero problema se includi più volte lo stesso elemento.
Pierre Arlaud,

Per i tipi di strutture ad albero, il nodo con il valore a volte non ha anche un campo "conta" che incrementa e diminuisce solo per i duplicati?
J Trana,

2

Penso che ci sia un malinteso qui.

A quanto ho capito, ogni nodo quadtree contiene un valore indicizzato da un punto. In altre parole, contiene il triplo (x, y, valore).

Contiene anche 4 puntatori a nodi figlio, che possono essere nulli. Esiste una relazione algoritmica tra le chiavi e i collegamenti figlio.

I tuoi inserti dovrebbero apparire così.

quadtree->insert(0.0, 0.0, value1)
quadtree->insert(0.0, 0.0, value2)

Il primo inserimento crea un nodo (padre) e inserisce un valore in esso.

Il secondo inserto crea un nodo figlio, si collega ad esso e inserisce un valore in esso (che può essere uguale al primo valore).

Quale nodo figlio viene istanziato dipende dall'algoritmo. Se l'algoritmo è nella forma [x) e lo spazio delle coordinate si trova nell'intervallo [0,1), ciascun figlio si estenderà all'intervallo [0,0,5) e il punto verrà posizionato nel figlio NO.

Non vedo alcuna ricorsione infinita.


Quindi stai dicendo che il mio modo di ridistribuire i nodi ai quadrifici durante la suddivisione è cosa c'è di sbagliato nella mia implementazione?
Pierre Arlaud,

Forse il problema è che stai cercando di spostare un valore da dove si trova (nel genitore) a un posto migliore (in un bambino). Non è davvero così. Il valore va bene dove si trova. Ma questo porta all'interessante risultato che due punti identici possono essere posizionati in nodi diversi (ma sempre correlati padre e figlio).
david.pfx il

2

La risoluzione comune che ho riscontrato (nei problemi di visualizzazione, non nei giochi) è quella di abbandonare uno dei punti, sostituendo sempre o mai sostituendo.

Suppongo che il punto principale a favore sia che sia facile da fare.


2

Suppongo che stai indicizzando elementi della stessa dimensione, altrimenti la vita diventa complessa, o lenta, o entrambi ...

Un nodo Quadtree non deve avere una capacità fissa. La capacità è usata per

  • Consentire a ciascun nodo dell'albero di avere dimensioni fisse in memoria o su disco - non è necessario se il nodo dell'albero contiene un insieme di elementi di dimensioni variabili e si utilizza un sistema di allocazione dello spazio che fa fronte. (Ad esempio oggetti java / c # in memoria.)
  • Decidi quando dividere un nodo.
    • Potresti semplicemente ridefinire la regola, in modo che un nodo sia diviso se contiene più di "n" elementi di distretto, in cui il distretto è definito in base alla posizione degli elementi.
    • Oppure usa un " elemento composito ", quindi se ci sono moltiplicare gli elementi nella stessa posizione, si introduce un nuovo elemento che contiene un elenco di questi elementi moltiplicati.

2

Quando hai a che fare con problemi di indicizzazione spaziale, in realtà ti consiglio di iniziare con un hash spaziale o il mio preferito personale: la semplice vecchia griglia.

inserisci qui la descrizione dell'immagine

... e comprenderne i punti deboli prima di passare alle strutture ad albero che consentono rappresentazioni sparse.

Uno degli ovvi punti deboli è che potresti sprecare memoria su molte celle vuote (anche se una griglia implementata in modo decente non dovrebbe richiedere più di 32 bit per cella a meno che tu non abbia effettivamente miliardi di nodi da inserire). Un altro è che se hai elementi di dimensioni moderate che sono più grandi delle dimensioni di una cella e spesso si estendono, diciamo, dozzine di celle, puoi sprecare molta memoria inserendo quegli elementi di medie dimensioni in molte più celle dell'ideale. Allo stesso modo quando si eseguono query spaziali, potrebbe essere necessario controllare più celle, a volte molto più, che l'ideale.

Ma l'unica cosa da affinare con una griglia per renderlo il più ottimale possibile rispetto a un determinato input è cell size, che non ti lascia troppo con cui pensare e giocherellare, ed è per questo che è la mia struttura di dati di riferimento per problemi di indicizzazione spaziale fino a quando non trovo motivi per non utilizzarlo. È semplice da implementare e non richiede di giocherellare con qualcosa di più di un singolo input di runtime.

Puoi ottenere molto da una semplice vecchia griglia e in realtà ho battuto molte implementazioni di quad-tree e kd tree usate nei software commerciali sostituendole con una vecchia griglia semplice (anche se non erano necessariamente le migliori implementate , ma gli autori hanno trascorso molto più tempo dei 20 minuti che ho impiegato per creare una griglia). Ecco una piccola cosa che ho preparato per rispondere a una domanda altrove usando una griglia per il rilevamento delle collisioni (nemmeno ottimizzata, solo poche ore di lavoro, e ho dovuto passare la maggior parte del tempo a imparare come funziona il pathfinding per rispondere alla domanda ed è stata anche la mia prima volta a implementare questo tipo di rilevamento delle collisioni):

inserisci qui la descrizione dell'immagine

Un'altra debolezza delle griglie (ma sono debolezze generali per molte strutture di indicizzazione spaziale) è che se si inseriscono molti elementi coincidenti o sovrapposti, come molti punti con la stessa posizione, verranno inseriti esattamente nelle stesse celle ) e peggiorare le prestazioni quando si attraversa quella cella. Allo stesso modo se si inseriscono molti elementi enormi che sono molto, molto più grandi delle dimensioni delle celle, vorranno essere inseriti in un carico di celle di celle e utilizzare molta memoria e degradare il tempo necessario per le query spaziali su tutta la linea .

Tuttavia, questi due problemi immediati sopra con elementi coincidenti e massicci sono in realtà problematici per tutte le strutture di indicizzazione spaziale. La semplice vecchia griglia in realtà gestisce questi casi patologici un po 'meglio di molti altri poiché almeno non vuole suddividere ripetutamente le cellule ripetutamente.

Quando inizi con la griglia e ti avvicini a qualcosa come un quad-tree o KD-tree, il problema principale che vuoi risolvere è il problema con l'inserimento di elementi in troppe celle, con troppe celle e / o dover controllare troppe celle con questo tipo di rappresentazione densa.

Ma se pensi a un quad-albero come a un'ottimizzazione su una grigliaper casi d'uso specifici, aiuta ancora a pensare all'idea di una "dimensione minima della cella", per limitare la profondità della suddivisione ricorsiva dei nodi quad-tree. Quando lo fai, lo scenario peggiore del quad-albero si degrada ancora nella fitta griglia alle foglie, solo meno efficiente della griglia poiché richiederà tempo logaritmico per spostarti dalla cella radice alla cella griglia anziché tempo costante. Tuttavia, pensare a quella dimensione minima della cella eviterà lo scenario di loop / ricorsione infinito. Per elementi massicci ci sono anche alcune varianti alternative come i quad-alberi sciolti che non si dividono necessariamente uniformemente e potrebbero avere AABB per nodi figlio che si sovrappongono. I BVH sono anche interessanti come strutture di indicizzazione spaziale che non suddividono uniformemente i loro nodi. Per elementi coincidenti contro strutture ad albero, l'importante è imporre un limite alla suddivisione (o come altri hanno suggerito, semplicemente respingerli o trovare un modo per trattarli come se non stessero contribuendo al numero univoco di elementi in una foglia quando si determina quando la foglia dovrebbe suddividere). Un albero Kd potrebbe anche essere utile se si prevedono input con molti elementi coincidenti, poiché è necessario considerare solo una dimensione quando si determina se un nodo deve essere diviso mediano.


Come aggiornamento per i quadrifici, qualcuno ha posto una domanda che era piuttosto ampia (ma mi piacciono quelli) su come renderli efficienti per il rilevamento delle collisioni, e ho finito per rovesciarmi su quello su come li implemento. Si deve anche rispondere alle vostre domande: stackoverflow.com/questions/41946007/...
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.