Hintergrund
Dieses Bild zeigt das Problem:
Ich kann den roten Kreis kontrollieren. Die Ziele sind die blauen Dreiecke. Die schwarzen Pfeile geben die Richtung an, in die sich die Ziele bewegen.
Ich möchte alle Ziele in der Mindestanzahl von Schritten sammeln.
In jeder Runde muss ich 1 Schritt nach links / rechts / oben oder unten bewegen.
In jeder Runde bewegen sich die Ziele auch 1 Schritt gemäß den Anweisungen auf dem Brett.
Demo
Ich habe hier auf Google Appengine eine spielbare Demo des Problems veröffentlicht .
Ich wäre sehr interessiert, wenn jemand die Zielpunktzahl übertreffen könnte, da dies zeigen würde, dass mein aktueller Algorithmus nicht optimal ist. (Eine Glückwunschbotschaft sollte gedruckt werden, wenn Sie dies schaffen!)
Problem
Mein aktueller Algorithmus skaliert sehr schlecht mit der Anzahl der Ziele. Die Zeit steigt exponentiell und für 16 Fische sind es bereits einige Sekunden.
Ich möchte die Antwort für Brettgrößen von 32 * 32 und mit 100 sich bewegenden Zielen berechnen.
Frage
Was ist ein effizienter Algorithmus (idealerweise in Javascript) zur Berechnung der Mindestanzahl von Schritten zum Sammeln aller Ziele?
Was ich versucht habe
Mein aktueller Ansatz basiert auf Memoisierung, ist aber sehr langsam und ich weiß nicht, ob er immer die beste Lösung liefert.
Ich löse das Teilproblem "Was ist die Mindestanzahl von Schritten, um einen bestimmten Satz von Zielen zu sammeln und an einem bestimmten Ziel zu landen?".
Das Teilproblem wird rekursiv gelöst, indem jede Auswahl für das zuvor besuchte Ziel untersucht wird. Ich gehe davon aus, dass es immer optimal ist, die vorherige Teilmenge von Zielen so schnell wie möglich zu erfassen und dann so schnell wie möglich von der Position, an der Sie gelandet sind, zum aktuellen Ziel zu wechseln (obwohl ich nicht weiß, ob dies eine gültige Annahme ist).
Dies führt dazu, dass n * 2 ^ n Zustände berechnet werden, die sehr schnell wachsen.
Der aktuelle Code wird unten angezeigt:
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;
}
Was ich bedacht habe
Einige Optionen, über die ich mich gewundert habe, sind:
Zwischenspeichern von Zwischenergebnissen. Die Entfernungsberechnung wiederholt viele Simulationen und Zwischenergebnisse können zwischengespeichert werden.
Ich denke jedoch nicht, dass dies eine exponentielle Komplexität verhindern würde.Ein A * -Suchalgorithmus, obwohl mir nicht klar ist, was eine angemessene zulässige Heuristik wäre und wie effektiv dies in der Praxis wäre.
Untersuchen Sie gute Algorithmen für das Problem des Handlungsreisenden und prüfen Sie, ob sie auf dieses Problem zutreffen.
Der Versuch zu beweisen, dass das Problem NP-schwer und daher unvernünftig ist, eine optimale Antwort darauf zu suchen.