fundo
Esta imagem ilustra o problema:
Eu posso controlar o círculo vermelho. Os alvos são os triângulos azuis. As setas pretas indicam a direção em que os alvos se moverão.
Quero coletar todos os alvos no número mínimo de etapas.
Cada curva devo mover 1 passo para a esquerda / direita / cima ou para baixo.
Cada vez, os alvos também se moverão 1 passo de acordo com as instruções mostradas no tabuleiro.
Demo
Eu coloquei uma demonstração jogável do problema aqui no Google appengine .
Eu estaria muito interessado se alguém pudesse bater a pontuação desejada, pois isso mostraria que meu algoritmo atual está abaixo do ideal. (Uma mensagem de parabéns deve ser impressa se você conseguir isso!)
Problema
Meu algoritmo atual escala muito mal com o número de alvos. O tempo aumenta exponencialmente e para 16 peixes já leva vários segundos.
Eu gostaria de calcular a resposta para tamanhos de placa de 32 * 32 e com 100 alvos móveis.
Questão
O que é um algoritmo eficiente (idealmente em Javascript) para calcular o número mínimo de etapas para coletar todos os alvos?
O que eu tentei
Minha abordagem atual é baseada no memoisation, mas é muito lenta e não sei se sempre gerará a melhor solução.
Eu resolvo o subproblema de "qual é o número mínimo de etapas para coletar um determinado conjunto de alvos e terminar em um determinado alvo?".
O subproblema é resolvido recursivamente examinando cada escolha do destino anterior que foi visitado. Presumo que seja sempre ideal coletar o subconjunto anterior de alvos o mais rápido possível e, em seguida, mover da posição em que você acabou para o alvo atual o mais rápido possível (embora eu não saiba se essa é uma suposição válida).
Isso resulta em n * 2 ^ n estados a serem calculados, que crescem muito rapidamente.
O código atual é mostrado abaixo:
var DX=[1,0,-1,0];
var DY=[0,1,0,-1];
// Return the location of the given fish at time t
function getPt(fish,t) {
var i;
var x=pts[fish][0];
var y=pts[fish][1];
for(i=0;i<t;i++) {
var b=board[x][y];
x+=DX[b];
y+=DY[b];
}
return [x,y];
}
// Return the number of steps to track down the given fish
// Work by iterating and selecting first time when Manhattan distance matches time
function fastest_route(peng,dest) {
var myx=peng[0];
var myy=peng[1];
var x=dest[0];
var y=dest[1];
var t=0;
while ((Math.abs(x-myx)+Math.abs(y-myy))!=t) {
var b=board[x][y];
x+=DX[b];
y+=DY[b];
t+=1;
}
return t;
}
// Try to compute the shortest path to reach each fish and a certain subset of the others
// key is current fish followed by N bits of bitmask
// value is shortest time
function computeTarget(start_x,start_y) {
cache={};
// Compute the shortest steps to have visited all fish in bitmask
// and with the last visit being to the fish with index equal to last
function go(bitmask,last) {
var i;
var best=100000000;
var key=(last<<num_fish)+bitmask;
if (key in cache) {
return cache[key];
}
// Consider all previous positions
bitmask -= 1<<last;
if (bitmask==0) {
best = fastest_route([start_x,start_y],pts[last]);
} else {
for(i=0;i<pts.length;i++) {
var bit = 1<<i;
if (bitmask&bit) {
var s = go(bitmask,i); // least cost if our previous fish was i
s+=fastest_route(getPt(i,s),getPt(last,s));
if (s<best) best=s;
}
}
}
cache[key]=best;
return best;
}
var t = 100000000;
for(var i=0;i<pts.length;i++) {
t = Math.min(t,go((1<<pts.length)-1,i));
}
return t;
}
O que eu considerei
Algumas opções que me perguntam são:
Cache de resultados intermediários. O cálculo da distância repete muitas simulações e os resultados intermediários podem ser armazenados em cache.
No entanto, não acho que isso impediria de ter complexidade exponencial.Um algoritmo de busca A *, embora não esteja claro para mim qual seria uma heurística admissível apropriada e quão eficaz isso seria na prática.
Investigar bons algoritmos para o problema do caixeiro-viajante e ver se eles se aplicam a este problema.
Tentar provar que o problema é NP-difícil e, portanto, irracional, buscar uma resposta ótima para ele.
fonte
Respostas:
Você pesquisou a literatura? Encontrei estes papéis que parecem analisar seu problema:
"Rastreamento de alvos móveis e o problema do caixeiro viajante não estacionário": http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.85.9940
"O problema do caixeiro viajante com alvo móvel": http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.57.6403
ATUALIZAÇÃO 1:
Os dois artigos acima parecem se concentrar no movimento linear para a métrica euclidiana.
fonte
Método ganancioso
Uma abordagem sugerida nos comentários é ir primeiro ao alvo mais próximo.
Eu coloquei uma versão da demonstração que inclui o custo calculado por meio desse método ganancioso aqui .
O código é:
Para 10 alvos, é cerca de duas vezes a distância ideal, mas às vezes muito mais (por exemplo, * 4) e ocasionalmente até atinge o ideal.
Essa abordagem é muito eficiente, então posso pagar alguns ciclos para melhorar a resposta.
Em seguida, estou pensando em usar métodos de colônia de formigas para ver se eles podem explorar o espaço de solução de forma eficaz.
Método de colônia de formigas
Um método de colônia de formigas parece funcionar muito bem para esse problema. O link nesta resposta agora compara os resultados ao usar o método da colônia gananciosa e da colônia de formigas.
A ideia é que as formigas escolham sua rota probabilisticamente com base no nível atual de feromônio. A cada 10 tentativas, depositamos feromônios adicionais ao longo da trilha mais curta que eles encontraram.
Resultados
Este método de colônia de formigas usando 100 repetições de 10 formigas ainda é muito rápido (37ms para 16 alvos em comparação com 3700ms para a busca exaustiva) e parece muito preciso.
A tabela abaixo mostra os resultados de 10 testes usando 16 alvos:
O método das formigas parece significativamente melhor do que o guloso e muitas vezes está muito próximo do ideal.
fonte
O problema pode ser representado em termos do Problema do Caixeiro Viajante Generalizado e então convertido em um Problema do Caixeiro Viajante convencional. Este é um problema bem estudado. É possível que as soluções mais eficientes para o problema do OP não sejam mais eficientes do que as soluções para o TSP, mas de forma alguma certas (provavelmente estou deixando de aproveitar alguns aspectos da estrutura do problema do OP que permitiriam uma solução mais rápida , como sua natureza cíclica). De qualquer forma, é um bom ponto de partida.
De C. Noon & J.Bean, Uma transformação eficiente do problema do caixeiro viajante generalizado :
Para o problema do OP:
N
é a localização de um determinado peixe em um determinado momento. Representa isso como(x, y, t)
, onde(x, y)
é uma coordenada de grade et
é o momento em que o peixe estará nesta coordenada. Para o peixe mais à esquerda no exemplo do OP, os primeiros (baseados em 1) são:(3, 9, 1), (4, 9, 2), (5, 9, 3)
conforme o peixe se move para a direita.fish(n_i)
retornar o ID do peixe representado pelo nó. Para quaisquer dois membros de N, podemos calcularmanhattan(n_i, n_j)
a distância de manhattan entre os dois nós etime(n_i, n_j
) para o deslocamento de tempo entre os nós.S_i
consistirá apenas nos nós para os quaisfish(n) == i
.i
ej
fish(n_i) != fish(n_j)
, em seguida, há um arco entrei
ej
.time(n_i, n_j)
, ou indefinido setime(n_i, n_j) < distance(n_i, n_j)
(ou seja, a localização não pode ser alcançada antes que o peixe chegue lá, talvez porque esteja para trás no tempo). Os arcos deste último tipo podem ser removidos.Resolver este problema resultaria em uma única visita a cada subconjunto de nós (ou seja, cada peixe é obtido uma vez) para um caminho com custo mínimo (ou seja, tempo mínimo para todos os peixes serem obtidos).
O artigo segue descrevendo como a formulação acima pode ser transformada em um problema tradicional do caixeiro viajante e, subsequentemente, resolvida ou aproximada com as técnicas existentes. Não li os detalhes, mas outro artigo que faz isso de uma forma que proclama ser eficiente é este .
Existem problemas óbvios com a complexidade. Em particular, o espaço do nó é infinito! Isso pode ser aliviado gerando nós apenas até um determinado horizonte de tempo. Se
t
for o número de passos de tempo para gerar nós ef
for o número de peixes, então o tamanho do espaço do nó serát * f
. Um nó por vezj
terá no máximo(f - 1) * (t - j)
arcos de saída (já que ele não pode voltar no tempo ou para seu próprio subconjunto). O número total de arcos será na ordem dost^2 * f^2
arcos. A estrutura do arco provavelmente pode ser arrumada, para aproveitar o fato de que os caminhos dos peixes são eventualmente cíclicos. Os peixes repetirão sua configuração uma vez a cada mínimo denominador comum de sua duração de ciclo, então talvez este fato possa ser usado.Não sei o suficiente sobre o TSP para dizer se isso é viável ou não, e não acho que isso significa que o problema postado é necessariamente NP-difícil ... mas é uma abordagem para encontrar uma solução ótima ou limitada .
fonte
Acho que outra abordagem seria:
Citar wikipedia:
Em matemática, um diagrama de Voronoi é uma maneira de dividir o espaço em várias regiões. Um conjunto de pontos (chamados sementes, locais ou geradores) é especificado de antemão e para cada semente haverá uma região correspondente que consiste em todos os pontos mais próximos daquela semente do que de qualquer outra.
Então, você escolhe um alvo, segue seu caminho por alguns passos e define um ponto de semente ali. Faça isso com todos os outros alvos também e você obterá um diagrama de voroni. Dependendo de qual área você está, você se move para o ponto de origem dela. Viola, você pegou o primeiro peixe. Agora repita este passo até que você tenha pensado em todos eles.
fonte