Eu tenho uma grade de peças de tamanho finito conhecido que forma um mapa. Algumas das peças dentro do mapa são colocadas em um conjunto conhecido como território. Este território está conectado, mas nada se sabe sobre sua forma. Na maioria das vezes, era um blob bastante regular, mas poderia ser muito alongado em uma direção e potencialmente até ter orifícios. Estou interessado em encontrar a fronteira (externa) do território.
Ou seja, eu quero uma lista de todos os ladrilhos que tocam em um dos ladrilhos no território sem ele próprio estar no território. Qual é uma maneira eficiente de encontrar isso?
Para uma dificuldade extra, acontece que meus blocos são hexágonos, mas suspeito que isso não faça muita diferença, cada bloco ainda é rotulado com uma coordenada inteira xey, e, dado um bloco, posso encontrar facilmente seus vizinhos. Abaixo estão alguns exemplos: o preto é o território e o azul a borda que quero encontrar. Isso por si só, não é um problema difícil. Um algoritmo simples para isso, em pseudo-python, é:
def find_border_of_territory(territory):
border = []
for tile in territory:
for neighbor in tile.neighbors():
if neighbor not in territory and neighbor not in border:
border.add(neighbor)
No entanto, isso é lento e eu gostaria de algo melhor. Eu tenho um loop O (n) sobre o território, outro loop (um curto, mas ainda) sobre todos os vizinhos e, em seguida, tenho que verificar a associação em duas listas, uma das quais é do tamanho n. Isso fornece uma terrível escala de O (n ^ 2). Posso reduzi-lo para O (n) usando conjuntos em vez de listas para fronteira e território, para que a associação seja rápida, mas ainda não é ótima. Espero que existam muitos casos em que o território é grande, mas a fronteira é pequena devido a uma simples área versus escala de linha. Por exemplo, se o território é um hexágono de raio 5, é do tamanho 91, mas a borda é apenas do tamanho 36.
Alguém pode propor algo melhor?
Editar:
Para responder a algumas das perguntas abaixo. O território pode variar em tamanho, de cerca de 20 a 100 ou mais. O conjunto de peças que formam o território é um atributo de um objeto, e é esse objeto que precisa de um conjunto de todas as peças de borda.
Inicialmente, o território é criado como um bloco e, em seguida, ganha principalmente peças uma a uma. Nesse caso, é verdade que a maneira mais rápida é manter um conjunto de bordas e atualizá-lo apenas no bloco ganho. Ocasionalmente, uma grande mudança no território pode acontecer - portanto, ele precisará ser recalculado completamente.
Agora sou da opinião de que fazer um algoritmo simples para encontrar fronteiras é a melhor solução. A única complexidade adicional que isso gera é garantir que a borda seja recalculada toda vez que for necessário, mas não mais do que isso. Estou bastante confiante de que isso pode ser feito de maneira confiável na minha estrutura atual.
Quanto ao tempo, no meu código atual, tenho algumas rotinas que precisam verificar todos os blocos do território. Nem todo turno, mas na criação e, ocasionalmente, depois. Isso leva mais de 50% do tempo de execução do meu código de teste, mesmo que seja uma parte muito pequena do programa completo. Eu estava, portanto, empenhado em minimizar qualquer repetição. NO ENTANTO, o código de teste envolve muito mais criação de objetos do que uma execução normal do programa (naturalmente), então eu sei que isso pode não ser muito relevante.