Фон
На этом рисунке показана проблема:
Я могу контролировать красный круг. Цели - это синие треугольники. Черные стрелки указывают направление движения мишеней.
Я хочу собрать все цели с минимальным количеством шагов.
В каждом повороте я должен двигаться на 1 шаг либо влево/вправо/вверх или вниз.
Каждый поворот цели также перемещается на 1 шаг в соответствии с указаниями, показанными на доске.
Demo
Я разместил демонстрационную версию проблемы здесь, в приложении Google appengine.
Мне было бы очень интересно, если кто-то может победить целевой счет, так как это покажет, что мой текущий алгоритм субоптимален. (Сообщение об поздравлении должно быть напечатано, если вы справитесь с этим!)
Проблема
Мой текущий алгоритм очень сильно масштабируется с количеством целей. Время увеличивается экспоненциально, а для 16 - это уже несколько секунд.
Я хотел бы вычислить ответ для размеров досок 32 * 32 и 100 движущихся целей.
Вопрос
Что такое эффективный алгоритм (идеально в Javascript) для вычисления минимального количества шагов для сбора всех целей?
Что я пробовал
Мой текущий подход основан на memoisation, но он очень медленный, и я не знаю, будет ли он всегда генерировать лучшее решение.
Я решаю подзадачу "какое минимальное количество шагов для сбора заданного набора целей и заканчивается на определенной цели?".
Подзадача рекурсивно решается путем изучения каждого выбора для предыдущей цели, которую вы посетили. Я полагаю, что всегда оптимально собирать предыдущее подмножество целей как можно быстрее, а затем быстро перемещаться от положения, в котором вы попали до текущей цели (хотя я не знаю, является ли это допустимым предположением).
Это приводит к вычислению n * 2 ^ n состояний, которое растет очень быстро.
Текущий код показан ниже:
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;
}
То, что я рассмотрел
Некоторые параметры, о которых я подумал, следующие:
-
Кэширование промежуточных результатов. Расчет расстояния повторяет много симуляции, и промежуточные результаты могут быть кэшированы.
Однако я не думаю, что это остановило бы его экспоненциальную сложность. -
Алгоритм поиска A *, хотя мне непонятно, какая подходящая допустимая эвристика будет и насколько эффективной будет это на практике.
-
Исследование хороших алгоритмов для проблемы коммивояжера и их применение к этой проблеме.
-
Попытка доказать, что проблема NP-жесткая и, следовательно, неразумно искать оптимальный ответ для нее.