Contexte
Cette image illustre le problème:
Je peux contrôler le cercle rouge. Les cibles sont les triangles bleus. Les flèches noires indiquent la direction dans laquelle les cibles se déplaceront.
Je souhaite collecter toutes les cibles en un minimum d'étapes.
À chaque tour, je dois faire un pas vers la gauche / droite / vers le haut ou vers le bas.
À chaque tour, les cibles se déplaceront également d'un pas selon les directions indiquées sur le tableau.
Démo
J'ai mis en place une démo jouable du problème ici sur Google appengine .
Je serais très intéressé si quelqu'un pouvait battre le score cible car cela montrerait que mon algorithme actuel est sous-optimal. (Un message de félicitations devrait être imprimé si vous gérez cela!)
Problème
Mon algorithme actuel évolue très mal avec le nombre de cibles. Le temps augmente de façon exponentielle et pour 16 poissons, il est déjà de plusieurs secondes.
Je voudrais calculer la réponse pour des tailles de carte de 32 * 32 et avec 100 cibles mobiles.
Question
Qu'est-ce qu'un algorithme efficace (idéalement en Javascript) pour calculer le nombre minimum d'étapes pour collecter toutes les cibles?
Ce que j'ai essayé
Mon approche actuelle est basée sur la mémoisation mais elle est très lente et je ne sais pas si elle générera toujours la meilleure solution.
Je résous le sous-problème de "quel est le nombre minimum d'étapes pour collecter un ensemble donné de cibles et aboutir à une cible particulière?".
Le sous-problème est résolu de manière récursive en examinant chaque choix de la cible précédente à avoir visité. Je suppose qu'il est toujours optimal de collecter le sous-ensemble précédent de cibles aussi rapidement que possible, puis de passer de la position où vous vous êtes retrouvé à la cible actuelle le plus rapidement possible (bien que je ne sache pas si c'est une hypothèse valable).
Il en résulte n * 2 ^ n états à calculer qui croît très rapidement.
Le code actuel est indiqué ci-dessous:
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;
}
Ce que j'ai considéré
Certaines options sur lesquelles je me suis interrogé sont:
Mise en cache des résultats intermédiaires. Le calcul de la distance répète de nombreuses simulations et les résultats intermédiaires peuvent être mis en cache.
Cependant, je ne pense pas que cela l'empêcherait d'avoir une complexité exponentielle.Un algorithme de recherche A * bien que je ne sache pas ce que serait une heuristique admissible appropriée et quelle serait son efficacité dans la pratique.
Enquêter sur de bons algorithmes pour le problème du voyageur de commerce et voir s'ils s'appliquent à ce problème.
Essayer de prouver que le problème est NP-difficile et donc déraisonnable de chercher une réponse optimale pour cela.
la source
Réponses:
Avez-vous recherché la littérature? J'ai trouvé ces papiers qui semblent analyser votre problème:
"Suivi des cibles mobiles et problème du voyageur de commerce non stationnaire": http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.85.9940
«Le problème du voyageur de commerce à cible mobile»: http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.57.6403
MISE À JOUR 1:
Les deux articles ci-dessus semblent se concentrer sur le mouvement linéaire pour la métrique euclidienne.
la source
Méthode gourmande
Une approche suggérée dans les commentaires consiste à aller d'abord vers la cible la plus proche.
J'ai mis en place une version de la démo qui inclut le coût calculé via cette méthode gourmande ici .
Le code est:
Pour 10 cibles, c'est environ le double de la distance optimale, mais parfois beaucoup plus (par exemple * 4) et parfois même atteint l'optimum.
Cette approche est très efficace et je peux donc me permettre quelques cycles pour améliorer la réponse.
Ensuite, j'envisage d'utiliser des méthodes de colonie de fourmis pour voir si elles peuvent explorer efficacement l'espace de solution.
Méthode de la colonie de fourmis
Une méthode de colonie de fourmis semble fonctionner remarquablement bien pour ce problème. Le lien dans cette réponse compare maintenant les résultats lors de l'utilisation de la méthode avide et de la colonie de fourmis.
L'idée est que les fourmis choisissent leur route de manière probabiliste en fonction du niveau actuel de phéromones. Tous les 10 essais, nous déposons des phéromones supplémentaires le long de la piste la plus courte trouvée.
Résultats
Cette méthode de colonie de fourmis utilisant 100 répétitions de 10 fourmis est toujours très rapide (37ms pour 16 cibles contre 3700ms pour la recherche exhaustive) et semble très précise.
Le tableau ci-dessous présente les résultats de 10 essais utilisant 16 cibles:
La méthode fourmi semble nettement meilleure que gourmande et souvent très proche de l'optimum.
la source
Le problème peut être représenté en termes de problème de voyageur de commerce généralisé, puis converti en problème de voyageur de commerce conventionnel. C'est un problème bien étudié. Il est possible que les solutions les plus efficaces au problème du PO ne soient pas plus efficaces que les solutions au TSP, mais en aucun cas certaines (je ne parviens probablement pas à tirer parti de certains aspects de la structure du problème du PO qui permettraient une solution plus rapide , comme sa nature cyclique). Quoi qu'il en soit, c'est un bon point de départ.
De C. Noon & J.Bean, Une transformation efficace du problème généralisé du voyageur de commerce :
Pour le problème du PO:
N
est l'emplacement d'un poisson particulier à un moment donné. Représentez-le comme(x, y, t)
, où(x, y)
est une coordonnée de grille, ett
est le moment auquel le poisson sera à cette coordonnée. Pour le poisson le plus à gauche dans l'exemple du PO, les premiers (basés sur 1) sont:(3, 9, 1), (4, 9, 2), (5, 9, 3)
lorsque le poisson se déplace vers la droite.fish(n_i)
retournons l'ID du poisson représenté par le nœud. Pour deux membres quelconques de N, nous pouvons calculermanhattan(n_i, n_j)
la distance de Manhattan entre les deux nœuds ettime(n_i, n_j
) le décalage temporel entre les nœuds.S_i
- ensemble disjoint comprendra uniquement les nœuds pour lesquelsfish(n) == i
.i
etj
fish(n_i) != fish(n_j)
alors il y a un arc entrei
etj
.time(n_i, n_j)
, ou indéfini sitime(n_i, n_j) < distance(n_i, n_j)
(c'est-à-dire que l'emplacement ne peut pas être atteint avant que le poisson y arrive, peut-être parce qu'il est en arrière dans le temps). Les arcs de ce dernier type peuvent être supprimés.La résolution de ce problème résulterait alors en une seule visite à chaque sous-ensemble de nœuds (c'est-à-dire que chaque poisson est obtenu une fois) pour un chemin avec un coût minimal (c'est-à-dire un temps minimal pour tous les poissons à obtenir).
L'article poursuit en décrivant comment la formulation ci-dessus peut être transformée en un problème de voyageur de commerce traditionnel et ensuite résolue ou rapprochée avec des techniques existantes. Je n'ai pas lu les détails, mais un autre article qui fait cela d'une manière qu'il proclame efficace est celui-ci .
Il y a des problèmes évidents de complexité. En particulier, l'espace des nœuds est infini! Cela peut être atténué en ne générant que des nœuds jusqu'à un certain horizon temporel. Si
t
est le nombre de pas de temps pour générer des nœuds etf
est le nombre de poissons, alors la taille de l'espace de nœuds serat * f
. Un nœud à la foisj
aura au plus des(f - 1) * (t - j)
arcs sortants (car il ne peut pas remonter dans le temps ou vers son propre sous-ensemble). Le nombre total d'arcs sera de l'ordre dest^2 * f^2
arcs. La structure en arc peut probablement être rangée, pour profiter du fait que les trajectoires des poissons sont finalement cycliques. Les poissons répéteront leur configuration une fois à chaque plus petit dénominateur commun de leurs longueurs de cycle, donc peut-être ce fait peut être utilisé.Je ne connais pas assez le TSP pour dire si cela est faisable ou non, et je ne pense pas que cela signifie que le problème affiché est nécessairement NP-difficile ... mais c'est une approche pour trouver une solution optimale ou limitée .
la source
Je pense qu'une autre approche serait:
Citer wikipedia:
En mathématiques, un diagramme de Voronoi est un moyen de diviser l'espace en un certain nombre de régions. Un ensemble de points (appelés graines, sites ou générateurs) est spécifié à l'avance et pour chaque graine, il y aura une région correspondante composée de tous les points plus proches de cette graine que de tout autre.
Ainsi, vous choisissez une cible, suivez son chemin pendant certaines étapes et définissez un point de départ là-bas. Faites-le également avec toutes les autres cibles et vous obtenez un diagramme voroni. Selon la zone dans laquelle vous vous trouvez, vous vous déplacez vers le point de départ de celui-ci. Viola, tu as le premier poisson. Maintenant, répétez cette étape jusqu'à ce que vous les ayez tous vaincus.
la source