A detecção de colisão é sempre O (n ^ 2)?


14

O mecanismo de física é capaz de diminuir essa complexidade, por exemplo, agrupando objetos que estão próximos um do outro e verificar se há colisões dentro deste grupo em vez de contra todos os objetos? (por exemplo, objetos distantes podem ser removidos de um grupo observando sua velocidade e distância de outros objetos).

Caso contrário, isso torna a colisão trivial para esferas (em 3d) ou disco (em 2d)? Devo fazer um loop duplo ou criar uma matriz de pares?

EDIT: Para motores de física como bullet e box2d, a detecção de colisão ainda é O (N ^ 2)?


12
Duas palavras: Particionamento espacial
MichaelHouse


1
Pode apostar. Acredito que ambos tenham implementações do SAP ( Sweep and Prune ) (entre outros), que é um algoritmo O (n log (n)). Pesquise "Detecção de colisão de fase ampla" para saber mais.
MichaelHouse

2
@ Byte56 O Sweep and Prune possui complexidade O (n log (n)) apenas se você precisar classificar sempre que testar. Você deseja manter uma lista ordenada de objetos e, sempre que adicionar um, basta classificá-lo no local correto O (log (n)) para obter O (log (n) + n) = O (n). Fica muito complicado quando os objetos começam a se mover!
9303 MartinTeeVarga

1
@ sm4, se os movimentos forem limitados, algumas passagens de tipo bolha podem resolver isso (basta marcar os objetos movidos e movê-los para frente ou para trás na matriz até que sejam ordenados. apenas observe outros objetos em movimento
catraca esquisita

Respostas:


14

A divisão espacial é sempre O (N ^ 2) no pior caso e é disso que trata a complexidade em informática.

No entanto, existem algoritmos que funcionam no tempo linear O (N) . Todos eles são baseados em algum tipo de linha de varredura.

Basicamente, você precisa ter seus objetos classificados por uma coordenada. Digamos X. Se você executar a classificação todas as vezes antes da detecção de colisão, a complexidade será O (N * logN). O truque é classificar somente quando você estiver adicionando objetos à cena e depois quando algo na cena mudar. Classificar após o movimento não é trivial. Veja o artigo vinculado abaixo para um algoritmo que leva em movimento e ainda funciona em tempo linear.

Então você varre da esquerda para a direita. Cada vez que sua linha de varredura cruza o início de um objeto, você a coloca em uma lista temporária. Sempre que sua linha de varredura sai do objeto, você a retira da lista. Você considera colisões apenas dentro desta lista temporária.

A linha de varredura ingênua também é O (N ^ 2) na pior das hipóteses (você faz com que todos os objetos abranjam todo o mapa da esquerda para a direita), mas você pode torná-lo O (N) tornando-o mais inteligente (veja o link abaixo). Um algoritmo realmente bom será bastante complexo.

Este é um diagrama simples de como a linha de varredura funciona:

Algoritmo de linha de varredura

A linha varre da esquerda para a direita. Os objetos são classificados pela coordenada X.

  • Caso um: Os dois primeiros objetos são verificados. Nada mais importa.
  • Caso dois: o primeiro objeto foi verificado e saiu da lista. Dois e três são verificados.
  • Caso três: Mesmo que esse objeto esteja colidindo, não verificamos.
  • Caso quatro: Porque verificamos neste caso!

Algoritmos como esse têm complexidade O (C * N) = O (N).

Fonte: Dois anos de cursos de geometria computacional.

Na detecção de colisões, isso geralmente é chamado de varredura e remoção , mas a família de algortitmos da linha de varredura é útil em muitos outros campos.

Outras leituras recomendadas que acredito estarem fora do escopo desta pergunta, mas, no entanto, são interessantes: Métodos eficientes de varredura e remoção em larga escala com inserção e remoção de AABB - Este artigo apresenta um algoritmo aprimorado de varredura e remoção que usa caixas delimitadoras alinhadas a eixos (AABB ) com a classificação que leva em consideração o movimento. O algoritmo apresentado no trabalho funciona em tempo linear.


Agora observe que este é o melhor algoritmo da teoria . Isso não significa que é usado. Na prática, o algoritmo O (N ^ 2) com divisão espacial terá melhor velocidade de desempenho em casos típicos (próximo a O (N)) e algum requisito extra para memória. Isso ocorre porque a constante C em O (C * N) pode ser muito alta! Como geralmente temos memória suficiente e casos típicos têm objetos espalhados uniformemente no espaço - esse algoritmo terá um desempenho MELHOR. Mas O (N) é a resposta para a pergunta original.


box2d / bullet usa isso?
Jokoon

3
"Varredura e poda" é o que normalmente é chamado de física. O bom é que você pode manter a classificação atualizada à medida que a simulação é avançada. Além disso, a linha de varredura em seu gráfico é um pouco diferente em termos de implementação (boa para a teoria) - você apenas iterava sobre as caixas de início / fim, para verificar apenas as possíveis colisões reais. Este método foi usado para gerar árvores de particionamento espacial mais capazes do que usado diretamente também.
Sean Middleditch

3
Como tecnicamente pode haver realmente colisões O (N ^ 2) em pares, não é inteiramente verdade dizer que varredura e remoção é sempre O (N). Em vez disso, a complexidade principal do algoritmo é O (N + c), onde c é o número de colisões encontradas pelo algoritmo - é sensível à saída , tanto quanto muitos algoritmos de casco convexo. (Referência: en.wikipedia.org/wiki/Output-sensitive_algorithm )
Steven Stadnicki 15/06/2013

1
Você deve apoiar suas reivindicações com algumas publicações ou pelo menos nomes de algoritmos.
Sam Hocevar

1
@SamHocevar Adicionei um link a um algoritmo Sweep and Prune realmente avançado que funciona em tempo linear com detalhamento detalhado das constantes. O fato de os algoritmos serem chamados "Sweep and Prune" era novo para mim, pois nunca trabalhei com ele. Eu usei esses algoritmos na seleção de mapas (que é uma colisão de 1 ponto com outros objetos), então apenas apliquei o conhecimento.
MartinTeeVarga

8

Não. A detecção de colisão nem sempre é O (N ^ 2).

Por exemplo, digamos que temos um espaço de 100x100 com objetos com tamanho 10x10. Poderíamos dividir esse espaço em células de 10x10 com uma grade.

Cada objeto pode estar em até 4 células da grade (pode caber em um bloco ou estar "entre" células). Poderíamos manter uma lista de objetos em cada célula.

Só precisamos verificar colisões nessas células. Se houver um número máximo de objetos por célula da grade (digamos, nunca há mais de 4 objetos no mesmo bloco), a detecção de colisão para cada objeto é O (1) e a detecção de colisão para todos os objetos é O (N).

Essa não é a única maneira de evitar a complexidade de O (N ^ 2). Existem outros métodos, mais adequados para outros casos de uso - geralmente usando estruturas de dados baseadas em árvore.

O algoritmo que descrevi é um tipo de particionamento espacial , mas existem outros algoritmos de particionamento espacial. Consulte Tipos de estruturas de dados de particionamento de espaço para obter mais algoritmos que evitam a complexidade temporal O (N ^ 2).

O Box2D e o Bullet suportam mecanismos para reduzir o número de pares verificados.

No manual , seção 4.15:

O processamento de colisão em uma etapa da física pode ser dividido em fase estreita e fase larga. Na fase estreita, calculamos pontos de contato entre pares de formas. Imagine que temos N formas. Usando força bruta, precisaríamos executar a fase estreita para pares N * N / 2.

A classe b2BroadPhase reduz essa carga usando uma árvore dinâmica para gerenciamento de pares. Isso reduz bastante o número de chamadas em fase estreita.

Normalmente você não interage diretamente com a fase ampla. Em vez disso, o Box2D cria e gerencia uma fase ampla internamente. Além disso, o b2BroadPhase foi projetado com o loop de simulação do Box2D em mente, portanto, provavelmente não é adequado para outros casos de uso.

Do Bullet Wiki :

Existem vários tipos de algoritmos de fase larga que aprimoram o ingênuo O (n ^ 2) que apenas retorna a lista completa de pares. Essas larguras otimizadas às vezes introduzem ainda mais pares não colidentes, mas isso é compensado pelo tempo de execução geralmente melhorado. Eles têm características de desempenho diferentes e nenhum supera os outros em todas as situações.

Árvore AABB dinâmica

Isso é implementado pela btDbvtBroadphase no Bullet.

Como o nome sugere, esta é uma árvore AABB dinâmica . Uma característica útil dessa fase larga é que a estrutura se adapta dinamicamente às dimensões do mundo e seu conteúdo. É muito bem otimizado e é uma banda larga de uso geral muito boa. Ele lida com mundos dinâmicos, onde muitos objetos estão em movimento, e a adição e remoção de objetos é mais rápida que o SAP.

Varredura e remoção (SAP)

No Bullet, este é o intervalo de classes do AxisSweep. Essa também é uma boa fase geral de uso geral, com a limitação de exigir um tamanho fixo do mundo, conhecido antecipadamente. Essa fase larga tem o melhor desempenho para mundos dinâmicos típicos, onde a maioria dos objetos tem pouco ou nenhum movimento. O btAxisSweep3 e o bt32AxisSweep3 quantizam os pontos inicial e final de cada eixo como números inteiros, em vez de números de ponto flutuante, para melhorar o desempenho.

O link a seguir é uma introdução geral à fase larga e também uma descrição do algoritmo Sweep and Prune (embora o chame de "Sort and Sweep"):

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

Além disso, dê uma olhada na página da wikipedia:

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


Alguns links para perguntas semelhantes e recursos externos tornariam essa uma ótima resposta.
MichaelHouse

3
Isto está errado. Você ainda está recebendo O (N ^ 2). Será muito mais rápido, algo como N ^ 2/100, mas ainda assim N ^ 2. Como prova, considere apenas que todos os objetos estão em uma célula.
9309 MartinTeeVarga

4
@ sm4 Este é o pior caso O (N ^ 2), que é realmente o que acontece se todos os objetos estiverem em uma célula. No entanto, em mecanismos de física, os objetos normalmente não estarão em uma célula. No meu exemplo, nenhum objeto pode compartilhar a mesma célula com mais de 3 outros objetos. Isso seria o que acontece em um mecanismo de física para objetos "normais" (e por "normal" quero dizer "não apenas um sensor").
luiscubal

Eu acho que o seu algoritmo exigiria o check-in nas 8 células ao redor, não apenas nas 4 células.
Jokoon

6
@luiscubal Complexidade é sempre o "pior caso". Em teoria, você está procurando complexidade "garantida". É o mesmo com quicksort, que é O (N ^ 2) e mergesort, que é O (N * logN). O Quicksort tem melhor desempenho em dados reais e tem menor exigência espacial. Mas o mergesort garantiu uma melhor complexidade. Se você precisar fazer alguma prova, use mergesort. Se você precisar classificar algo, use o quicksort.
9303 MartinTeeVarga

2

O (N ^ 2) refere-se ao fato de que se você tiver N objetos, descobrir o que está colidindo com o que é, na pior das hipóteses , os cálculos de colisão N ^ 2. Digamos que você tenha 3 objetos. Para encontrar "quem está acertando quem", você deve encontrar:

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

São 6 verificações de colisões ou N * (N-1). Na análise assintótica, expandiríamos o polinômio e aproximaríamos como O (N ^ 2). Se você tivesse 100 objetos, seria 100 * 99, o que é próximo o suficiente para 100 * 100.

Portanto, se você particionar espaço usando uma octree, por exemplo, o número médio de comparações entre corpos é reduzido. Se for possível que todos os objetos se agrupem em uma área muito pequena (digamos, se você estiver fazendo algum tipo de simulação de fluxo de partículas, onde partículas podem se reunir na mesma área), o O (N ^ 2) ainda poderá ocorrer em pontos na simulação (nos quais você verá a desaceleração).

Portanto, todo o ponto de O (N ^ 2) existe devido à natureza de cada corpo que verifica todos os outros corpos na cena. Essa é apenas a natureza da computação. Muitas coisas podem ajudar a tornar isso mais barato. Mesmo um gráfico de cena (digamos, detectar apenas objetos na mesma sala ) reduzirá significativamente o número de cálculos de colisão a serem feitos, mas ainda será O (M ^ 2) (onde M é o número de objetos na sala a ser detecção de colisão). Os volumes delimitadores esféricos tornam a verificação inicial muito rápida ( if( distance( myCenter, hisCenter ) > (myRadius+hisRadius) ) then MISS), portanto, mesmo que a detecção de colisão seja O (N ^ 2), é provável que os cálculos da esfera delimitadora ocorram muito rapidamente.


Não há necessidade de tomar a verificação de força bruta como referência: independentemente de algoritmos inteligentes, N objetos podem colidir com todos os outros objetos, fornecendo colisões O (N ^ 2) que exigem que o trabalho O (N ^ 2) seja processado. Bons algoritmos só podem fazer melhor quando há menos colisões.
Lorenzo Gatti
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.