Il rilevamento delle collisioni è sempre O (n ^ 2)?


14

Il motore fisico è in grado di ridurre tale complessità, ad esempio raggruppando oggetti vicini l'uno all'altro e verificando le collisioni all'interno di questo gruppo anziché contro tutti gli oggetti? (ad esempio, gli oggetti lontani possono essere rimossi da un gruppo osservandone la velocità e la distanza da altri oggetti).

In caso contrario, ciò rende la collisione banale per le sfere (in 3d) o per il disco (in 2d)? Dovrei fare un doppio ciclo o creare invece una matrice di coppie?

EDIT: per motori fisici come bullet e box2d, il rilevamento delle collisioni è ancora O (N ^ 2)?


12
Due parole: il partizionamento spaziale
MichaelHouse


1
Scommetti. Credo che entrambi abbiano implementazioni di SAP ( Sweep e Prune ) (tra gli altri) che è un algoritmo O (n log (n)). Cerca "Rilevamento collisioni a fase larga" per saperne di più.
MichaelHouse

2
@ Byte56 Sweep and Prune presenta complessità O (n log (n)) solo se è necessario ordinare ogni volta che si esegue il test. Vuoi mantenere un elenco ordinato di oggetti e ogni volta che ne aggiungi uno, basta ordinarlo nella posizione corretta O (log (n)) quindi ottieni O (log (n) + n) = O (n). Tuttavia, diventa molto complicato quando gli oggetti iniziano a muoversi!
MartinTeeVarga,

1
@ sm4, se i movimenti sono limitati, allora alcuni passaggi di ordinamento a bolle possono occuparsene (basta contrassegnare gli oggetti spostati e spostarli in avanti o indietro nell'array fino a quando non vengono ordinati. Basta fare attenzione agli altri oggetti di movimento
maniaco del cricchetto

Risposte:


14

La divisione spaziale è sempre O (N ^ 2) nel peggiore dei casi ed è ciò che riguarda la complessità dell'informatica.

Tuttavia ci sono algoritmi che funzionano nel tempo lineare O (N) . Tutti sono basati su una sorta di linea di spazzata.

Fondamentalmente devi avere i tuoi oggetti ordinati per una coordinata. Diciamo X. Se esegui l'ordinamento ogni volta prima del rilevamento delle collisioni, la complessità sarà O (N * logN). Il trucco è ordinare solo quando si aggiungono oggetti alla scena e successivamente quando qualcosa nella scena cambia. L'ordinamento dopo il movimento non è banale. Vedi il documento collegato di seguito per un algoritmo che prende in movimento e funziona ancora in tempo lineare.

Quindi spazza da sinistra a destra. Ogni volta che la tua linea di sweep incrocia l'inizio di un oggetto, lo metti in un elenco temporaneo. Ogni volta che la linea di sweep esce dall'oggetto, lo si estrae dall'elenco. Si considerano le collisioni solo all'interno di questo elenco temporaneo.

La linea di sweep ingenua è O (N ^ 2) anche nel peggiore dei casi (fai in modo che tutti gli oggetti si estendano sull'intera mappa da sinistra a destra), ma puoi renderla O (N) rendendola più intelligente (vedi link sotto). Un algoritmo davvero valido sarà piuttosto complesso.

Questo è un semplice diagramma di come funziona la linea di sweep:

Algoritmo della linea di sweep

La linea scorre da sinistra a destra. Gli oggetti sono ordinati per coordinata X.

  • Caso 1: vengono controllati i primi due oggetti. Non importa nient'altro.
  • Caso due: il primo oggetto è stato verificato e non è più presente nell'elenco. Due e tre sono controllati.
  • Caso tre: anche se quell'oggetto si scontra, non controlliamo.
  • Caso quattro: perché controlliamo in questo caso!

Algoritmi come questo hanno complessità O (C * N) = O (N).

Fonte: due anni di corsi di geometria computazionale.

Nel rilevamento delle collisioni questo è in genere chiamato Sweep e Prune , ma la famiglia di algortitmi della linea di sweep è utile in molti altri campi.

Ulteriori letture consigliate che ritengo non rientrino nell'ambito di questa domanda, ma comunque interessanti: Metodi di spazzata e prugna su larga scala efficienti con inserimento e rimozione di AABB - Questo documento presenta un algoritmo di spazzata e prugna migliorato che utilizza scatole di delimitazione allineate agli assi (AABB ) con l'ordinamento che tiene conto del movimento. L'algoritmo presentato nel documento funziona in tempo lineare.


Ora nota che questo è il miglior algoritmo in teoria . Non significa che sia usato. In pratica, l'algoritmo O (N ^ 2) con divisione spaziale avrà una migliore velocità in termini di prestazioni nel caso tipico (vicino a O (N)) e alcuni requisiti extra per la memoria. Questo perché la costante C in O (C * N) può essere molto alta! Poiché di solito abbiamo memoria sufficiente e casi tipici hanno oggetti distribuiti uniformemente nello spazio - tale algoritmo renderà MIGLIORE. Ma O (N) è la risposta alla domanda originale.


box2d / bullet usa questo?
jokoon,

3
"Sweep and prune" è ciò che normalmente viene chiamato per la fisica. La cosa bella è che puoi mantenere aggiornato l'ordinamento man mano che la simulazione è avanzata. Inoltre, la linea di sweep nel tuo grafico è un po 'fuori dal punto di vista dell'implementazione (buono per la teoria però) - dovresti semplicemente iterare sugli inizi / fine del riquadro, quindi controllerai solo le potenziali collisioni effettive. Visto questo metodo usato per generare alberi di partizionamento spaziale più capaci piuttosto che usato anche direttamente.
Sean Middleditch,

3
Dal momento che tecnicamente possono effettivamente esserci collisioni O (N ^ 2) in coppia, non è del tutto vero dire che spazzare e potare è sempre O (N). Piuttosto, la complessità principale dell'algoritmo è O (N + c), dove c è il numero di collisioni rilevate dall'algoritmo - è sensibile all'output , così come lo sono molti algoritmi dello scafo convesso. (Riferimento: en.wikipedia.org/wiki/Output-sensitive_algorithm )
Steven Stadnicki,

1
Dovresti sostenere i tuoi reclami con alcune pubblicazioni o almeno nomi di algoritmi.
sam hocevar,

1
@SamHocevar Ho aggiunto un collegamento a un algoritmo Sweep and Prune davvero avanzato che funziona in tempo lineare con una suddivisione dettagliata delle costanti. Il fatto che gli algoritmi siano chiamati "Sweep and Prune" era una novità per me, dato che non ci avevo mai lavorato. Ho usato questi algoritmi nella selezione della mappa (che è una specie di collisione di 1 punto con altri oggetti), quindi ho appena applicato la conoscenza.
MartinTeeVarga

8

No. Il rilevamento delle collisioni non è sempre O (N ^ 2).

Ad esempio, supponiamo di avere uno spazio 100x100 con oggetti con dimensioni 10x10. Potremmo dividere questo spazio in celle di 10x10 con una griglia.

Ogni oggetto può trovarsi in un massimo di 4 celle della griglia (potrebbe adattarsi perfettamente a un blocco o essere "tra" celle). Potremmo tenere un elenco di oggetti in ogni cella.

Dobbiamo solo verificare la presenza di collisioni in quelle celle. Se esiste un numero massimo di oggetti per cella della griglia (ad esempio, non ci sono mai più di 4 oggetti nello stesso blocco), il rilevamento delle collisioni per ogni oggetto è O (1) e il rilevamento delle collisioni per tutti gli oggetti è O (N).

Questo non è l'unico modo per evitare la complessità O (N ^ 2). Esistono altri metodi, più adeguati per altri casi d'uso, che spesso utilizzano strutture di dati basate su alberi.

L'algoritmo che ho descritto è un tipo di partizionamento spazio , ma ci sono altri algoritmi di partizionamento dello spazio. Vedere Tipi di strutture di dati di partizionamento dello spazio per alcuni algoritmi in più che evitano la complessità temporale di O (N ^ 2).

Box2D e Bullet supportano meccanismi per ridurre il numero di coppie verificate.

Dal manuale , sezione 4.15:

L'elaborazione delle collisioni in una fase della fisica può essere suddivisa in fase stretta e fase larga. Nella fase stretta calcoliamo i punti di contatto tra coppie di forme. Immagina di avere N forme. Usando la forza bruta, avremmo bisogno di eseguire la fase stretta per coppie N * N / 2.

La classe b2BroadPhase riduce questo carico utilizzando un albero dinamico per la gestione delle coppie. Ciò riduce notevolmente il numero di chiamate in fase stretta.

Normalmente non interagisci direttamente con la fase larga. Invece, Box2D crea e gestisce internamente una fase larga. Inoltre, b2BroadPhase è progettato pensando al loop di simulazione di Box2D, quindi probabilmente non è adatto per altri casi d'uso.

Dal Bullet Wiki :

Esistono vari tipi di algoritmi a larga fase che migliorano l'algoritmo O (n ^ 2) ingenuo che restituisce solo l'elenco completo di coppie. Queste fasi ottimizzate a volte introducono ancora più coppie non collidenti, ma ciò è compensato dal tempo di esecuzione generalmente migliorato. Hanno caratteristiche prestazionali diverse e nessuna supera le altre in tutte le situazioni.

Albero dinamico AABB

Questo è implementato da btDbvtBroadphase in Bullet.

Come suggerisce il nome, questo è un albero AABB dinamico . Una caratteristica utile di questa fase larga è che la struttura si adatta dinamicamente alle dimensioni del mondo e ai suoi contenuti. È ottimizzato molto bene e un'ottima fase per scopi generici. Gestisce mondi dinamici in cui molti oggetti sono in movimento e l'aggiunta e la rimozione di oggetti è più rapida di SAP.

Sweep and Prune (SAP)

In Bullet, questa è la gamma di classi AxisSweep. Questa è anche una buona fase generica, con la limitazione che richiede una dimensione mondiale fissa, nota in anticipo. Questa fase ha le migliori prestazioni per mondi tipici della dinamica, in cui la maggior parte degli oggetti ha poco o nessun movimento. Sia btAxisSweep3 che bt32AxisSweep3 quantizzano i punti di inizio e fine per ciascun asse come numeri interi anziché numeri in virgola mobile, per migliorare le prestazioni.

Il seguente link è un'introduzione generale alla fase larga e anche una descrizione dell'algoritmo Sweep and Prune (sebbene lo chiami "Sort and Sweep"):

http://www.ziggyware.com/readarticle.php?article_id=128

Inoltre, dai un'occhiata alla pagina di Wikipedia:

http://en.wikipedia.org/wiki/Sweep_and_prune


Alcuni collegamenti a domande simili e risorse esterne renderebbero questa un'ottima risposta.
MichaelHouse

3
Questo è sbagliato. Stai ancora ricevendo O (N ^ 2). Sarà molto più veloce, qualcosa come N ^ 2/100, ma comunque N ^ 2. Come prova, considera che tutti gli oggetti si trovano in una cella.
MartinTeeVarga,

4
@ sm4 Questo è il caso peggiore O (N ^ 2), che è effettivamente ciò che accade se tutti gli oggetti si trovano in una cella. Tuttavia, in un motore fisico, gli oggetti in genere non si trovano in una cella. Nel mio esempio, nessun oggetto può mai condividere la stessa cella con più di 3 altri oggetti. Questo sarebbe ciò che accade in un motore fisico per oggetti "normali" (e per "normale" intendo "non solo un sensore").
luiscubal,

Penso che il tuo algoritmo richiederebbe di controllare le 8 celle intorno, non solo le 4 celle.
jokoon,

6
La complessità di @luiscubal è sempre il "caso peggiore". In teoria stai cercando complessità "garantita". È lo stesso con quicksort, che è O (N ^ 2) e mergesort, che è O (N * logN). Quicksort offre prestazioni migliori su dati reali e ha requisiti spaziali inferiori. Ma mergesort ha garantito una migliore complessità. Se hai bisogno di provare qualcosa, usa mergesort. Se devi ordinare qualcosa, usa quicksort.
MartinTeeVarga,

2

O (N ^ 2) si riferisce al fatto che se si hanno N oggetti, capire cosa si sta scontrando con ciò che è, nel peggiore dei casi , calcoli di collisione N ^ 2. Supponi di avere 3 oggetti. Per trovare "chi sta colpendo chi", devi trovare:

o1 hitting o2?  o1 hitting o3?
o2 hitting o1?  o2 hitting o3?
o3 hitting o1?  o3 hitting o2?

Sono 6 assegni per collisioni o assegni N * (N-1). Nell'analisi asintotica espandiamo il polinomio e approssimiamo come O (N ^ 2). Se avessi 100 oggetti, allora sarebbe 100 * 99, che è abbastanza vicino a 100 * 100.

Pertanto, se si suddivide lo spazio usando ad esempio un ottetto, il numero medio di confronti tra corpi è ridotto. Se è possibile che tutti gli oggetti si riuniscano in un'area molto piccola (ad esempio se si sta eseguendo una sorta di simulazione del flusso di particelle, in cui le particelle possono accumularsi nella stessa area), allora O (N ^ 2) può ancora verificarsi in punti nella simulazione (in quali punti vedrai un rallentamento).

Quindi, l'intero punto di O (N ^ 2) è dovuto alla natura di ogni corpo che controlla ogni altro corpo nella scena. Questa è solo la natura del calcolo. Molte cose possono aiutare a rendere questo più economico però. Anche un grafico di scena (diciamo solo il rilevamento tra oggetti nella stessa stanza ) ridurrà il numero di calcoli di collisione da eseguire in modo significativo, ma sarà comunque O (M ^ 2) (dove M è il numero di oggetti nella stanza a essere rilevato collisione contro). I volumi di delimitazione sferica rendono il controllo iniziale molto veloce ( if( distance( myCenter, hisCenter ) > (myRadius+hisRadius) ) then MISS), quindi anche se il rilevamento delle collisioni è O (N ^ 2), è probabile che i calcoli della sfera di delimitazione avvengano molto velocemente.


Non è necessario prendere il controllo della forza bruta come riferimento: indipendentemente dagli algoritmi intelligenti, N oggetti possono scontrarsi con tutti gli altri oggetti, dando a O (N ^ 2) le collisioni che richiedono l'elaborazione di O (N ^ 2). I buoni algoritmi possono fare di meglio solo quando ci sono meno collisioni.
Lorenzo Gatti,
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.