Quadtree com duplicatas


10

Estou implementando um quadtree. Para quem não conhece essa estrutura de dados, estou incluindo a seguinte pequena descrição:

Um Quadtree é uma estrutura de dados e está no plano euclidiano o que um Octree é em um espaço tridimensional. Um uso comum de quadras é a indexação espacial.

Para resumir como eles funcionam, um quadtree é uma coleção - digamos retângulos aqui - com uma capacidade máxima e uma caixa delimitadora inicial. Ao tentar inserir um elemento em um quadtree que tenha atingido sua capacidade máxima, o quadtree é subdividido em 4 quadtrees (cuja representação geométrica terá uma área quatro vezes menor que a árvore antes da inserção); cada elemento é redistribuído nas subárvores de acordo com sua posição, ie. o canto superior esquerdo vinculado ao trabalhar com retângulos.

Portanto, uma quadtree é uma folha e tem menos elementos do que sua capacidade, ou uma árvore com quatro quadras quando criança (geralmente noroeste, nordeste, sudoeste, sudeste).

Minha preocupação é que, se você tentar adicionar duplicatas, seja o mesmo elemento várias vezes ou vários elementos diferentes com a mesma posição, os quadríceps têm um problema fundamental no manuseio das bordas.

Por exemplo, se você trabalha com um quadtree com capacidade de 1 e o retângulo de unidade como a caixa delimitadora:

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

E você tenta inserir duas vezes um retângulo cujo limite superior esquerdo é a origem: (ou da mesma forma, se você tentar inseri-lo N + 1 vezes em um quadtree com uma capacidade de N> 1)

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

A primeira inserção não será um problema: Primeira inserção

Mas a primeira inserção acionará uma subdivisão (porque a capacidade é 1): Segunda inserção, primeira subdivisão

Ambos os retângulos são colocados na mesma subárvore.

Então, novamente, os dois elementos chegarão no mesmo quadtree e acionarão uma subdivisão… Segunda inserção, segunda subdivisão

E assim por diante, e assim por diante, o método de subdivisão será executado indefinidamente porque (0, 0) estará sempre na mesma subárvore dentre as quatro criadas, o que significa que ocorre um problema de recursão infinito.

É possível ter um quadtree com duplicatas? (Caso contrário, pode-se implementá-lo como a Set)

Como podemos resolver esse problema sem quebrar completamente a arquitetura de uma quadtree?


Como você gostaria que se comportasse? Você está implementando, então você precisa decidir qual comportamento é correto para você. Talvez cada coordenada exclusiva possa ser uma lista de elementos nessa coordenada. Talvez seus pontos sejam limitados para serem únicos. Você sabe o que precisa e nós não.
Inutil

@ Useless Isso é muito verdade. No entanto, deve ter havido muita pesquisa sobre o assunto e também não quero reinventar a roda. TBH eu ainda não sei se esta questão pertence mais no SO, em programmers.SE, em gamedev.SE ou mesmo em math.SE ...
Pierre Arlaud

Respostas:


3

Você está implementando uma estrutura de dados e, portanto, precisa tomar decisões de implementação.

A menos que o quadtree tenha algo específico a dizer sobre exclusividade - e não estou ciente disso -, essa é uma decisão de implementação. É ortogonal à definição de um quadtree e você pode lidar com isso da maneira que desejar. O quadtree mostra como inserir e atualizar chaves, mas não se elas precisam ser exclusivas ou o que você pode conectar a cada nó.

Tomar decisões de implementação não é reinventar a roda , pelo menos não mais do que escrever sua própria implementação em primeiro lugar.

Para comparação, a biblioteca padrão C ++ oferece um conjunto exclusivo, um multiset não exclusivo, um mapa exclusivo (essencialmente um conjunto de pares de valores-chave ordenados e comparados apenas pela chave) e um multimapa não exclusivo. Eles são tipicamente implementados usando a mesma árvore vermelho-preta e nenhuma está quebrando a arquitetura , simplesmente porque a definição da árvore vermelho-preta não tem nada a dizer sobre a exclusividade das chaves ou os tipos armazenados nos nós das folhas.

Finalmente, se você acha que há uma pesquisa sobre isso, encontre-a e então podemos discuti-la. Talvez haja algum invariante quadtree que eu tenha esquecido, ou alguma restrição adicional que permita melhor desempenho.


Meu problema é que não consigo encontrar nenhuma documentação declarando que a exclusividade é um requisito. No entanto, se você viu meu exemplo, pode ver que é um problema real se incluir várias vezes o mesmo elemento.
Pierre Arlaud

Para tipos de estruturas em árvore, o nó com o valor às vezes também não recebe um campo "count" que apenas aumenta e diminui para duplicatas?
J Trana

2

Eu acho que há um mal-entendido aqui.

Pelo que entendi, todo nó quadtree contém um valor indexado por um ponto. Em outras palavras, ele contém o triplo (x, y, valor).

Ele também contém 4 ponteiros para nós filhos, que podem ser nulos. Existe uma relação algorítmica entre as chaves e os links filhos.

Suas inserções devem ficar assim.

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

A primeira inserção cria um nó (pai) e insere um valor nele.

A segunda inserção cria um nó filho, vincula-o e insere um valor nele (que pode ser o mesmo que o primeiro valor).

Qual nó filho é instanciado depende do algoritmo. Se o algoritmo estiver no formato [x) e o espaço de coordenadas estiver no intervalo [0,1), então cada filho ultrapassará o intervalo [0,0,5) e o ponto será colocado no filho NW.

Não vejo recursão infinita.


Então, você está dizendo minha maneira de redistribuir os nós para os quadríceps filhos quando subdividir, o que há de errado com minha implementação?
Pierre Arlaud

Talvez o problema seja que você está tentando mover um valor de onde ele está (nos pais) para um lugar melhor (em uma criança). Realmente não é assim que é feito. O valor é bom onde está. Mas isso leva ao resultado interessante de que dois pontos idênticos podem ser colocados em nós diferentes (mas sempre pai e filho relacionados).
David.pfx

2

A resolução comum que encontrei (em problemas de visualização, não em jogos) é abandonar um dos pontos, sempre substituindo ou nunca substituindo.

Suponho que o principal ponto a favor é que é fácil de fazer.


2

Estou assumindo que você está indexando elementos que são aproximadamente do mesmo tamanho, caso contrário, a vida se torna complexa, lenta ou ambas ...

Um nó Quadtree não precisa ter uma capacidade fixa. A capacidade é usada para

  • Permita que cada nó da árvore tenha tamanho fixo na memória ou no disco - não é necessário se o nó da árvore contiver um conjunto de elementos de tamanho variável e você estiver usando um sistema de alocação de espaço que lide. (Por exemplo, objetos java / c # na memória.)
  • Decida quando dividir um nó.
    • Você pode apenas redefinir a regra, para que um nó seja dividido se contiver mais que "n" elementos de distrito, em que distrito é definido de acordo com a localização dos elementos.
    • Ou use um " elemento composto ", portanto, se houver elementos de multiplicação no mesmo local, você introduz um novo elemento que contém uma lista desses elementos de multiplicação.

2

Quando você está lidando com problemas de indexação espacial, recomendo começar com um hash espacial ou o meu favorito: a grade antiga simples.

insira a descrição da imagem aqui

... e entenda suas fraquezas antes de passar para estruturas em árvore que permitem representações esparsas.

Uma das fraquezas óbvias é que você pode desperdiçar memória em muitas células vazias (embora uma grade implementada decentemente não exija mais de 32 bits por célula, a menos que você tenha bilhões de nós para inserir). Outra é que, se você possui elementos de tamanho moderado, maiores que o tamanho de uma célula e geralmente abrangem, digamos, dezenas de células, pode gastar muita memória inserindo esses elementos de tamanho médio em muito mais células do que o ideal. Da mesma forma, quando você faz consultas espaciais, pode ser necessário verificar mais células, às vezes muito mais, do que o ideal.

Mas a única coisa que precisa de uma grade para torná-la a mais otimizada possível contra uma determinada entrada é cell size, o que não deixa muito para você pensar e mexer, e é por isso que é a minha estrutura de dados preferida para problemas de indexação espacial até encontrar razões para não usá-lo. É muito simples de implementar e não exige que você mexa em nada além de uma única entrada de tempo de execução.

Você pode tirar muito proveito de uma grade antiga simples e, na verdade, derrotei muitas implementações de árvore quádrupla e kd usadas em software comercial, substituindo-as por uma grade antiga simples (embora elas não fossem necessariamente as melhores implementadas , mas os autores gastaram muito mais tempo do que os 20 minutos que gastei para criar uma grade). Aqui está uma coisinha rápida que preparei para responder a uma pergunta em outro lugar usando uma grade para detecção de colisão (nem mesmo realmente otimizada, apenas algumas horas de trabalho, e eu tive que gastar a maior parte do tempo aprendendo como o pathfinding funciona para responder à pergunta e também foi a primeira vez que implementei esse tipo de detecção de colisão):

insira a descrição da imagem aqui

Outra fraqueza das grades (mas são fraquezas gerais para muitas estruturas de indexação espacial) é que, se você inserir muitos elementos coincidentes ou sobrepostos, como muitos pontos com a mesma posição, eles serão inseridos na mesma célula (s) ) e degradam o desempenho ao atravessar essa célula. Da mesma forma, se você inserir muitos elementos maciços muito, muito maiores que o tamanho da célula, eles desejarão ser inseridos em um bando de células e usarão muita memória e degradarão o tempo necessário para consultas espaciais em todo o quadro .

No entanto, esses dois problemas imediatos acima, com elementos coincidentes e maciços, são realmente problemáticos para todas as estruturas de indexação espacial. Na verdade, a grade antiga lida com esses casos patológicos um pouco melhor do que muitos outros, pois pelo menos não deseja subdividir recursivamente as células repetidamente.

Quando você começa com a grade e trabalha em direção a algo como uma árvore quádrupla ou KD, o principal problema que você deseja resolver é o problema com os elementos sendo inseridos em muitas células, com muitas células e / ou ter que verificar muitas células com esse tipo de representação densa.

Mas se você pensar em um quad-tree como uma otimização em uma gradepara casos de uso específicos, é útil ainda pensar na idéia de um "tamanho mínimo de célula" para limitar a profundidade da subdivisão recursiva dos nós das quatro árvores. Quando você faz isso, o pior cenário da quad-árvore ainda se degradará na grade densa nas folhas, apenas menos eficiente que a grade, pois exigirá tempo logarítmico para percorrer o caminho da raiz à célula da grade em vez de tempo constante. No entanto, pensar nesse tamanho mínimo de célula evitará o cenário de loop / recursão infinito. Para elementos maciços, também existem algumas variantes alternativas, como quad-trees soltas que não necessariamente se dividem igualmente e poderiam ter AABBs para nós filhos que se sobrepõem. BVHs também são interessantes como estruturas de indexação espacial que não subdividem uniformemente seus nós. Para elementos coincidentes contra estruturas de árvores, o principal é simplesmente impor um limite à subdivisão (ou, como outros sugeriram, apenas rejeitá-las ou encontrar uma maneira de tratá-las como se não estivessem contribuindo para o número único de elementos em uma folha ao determinar quando a folha deve subdividir). Uma árvore Kd também pode ser útil se você antecipar entradas com muitos elementos coincidentes, pois é necessário considerar apenas uma dimensão ao determinar se um nó deve dividir a mediana.


Como uma atualização para os quadtrees, alguém fez uma pergunta que era meio ampla (mas eu gosto disso) sobre como torná-los eficientes para a detecção de colisões, e acabei me dedicando a isso sobre como implementá-los. Ele também deve responder às suas perguntas: stackoverflow.com/questions/41946007/…
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.