Задний план
Это изображение иллюстрирует проблему:
Я могу контролировать красный круг. Цели - синие треугольники. Черные стрелки указывают направление, в котором будут двигаться цели.
Я хочу собрать все мишени за минимальное количество шагов.
Каждый ход я должен делать 1 шаг влево / вправо / вверх или вниз.
Каждый ход мишени также перемещаются на 1 шаг в соответствии с указаниями на доске.
Демо
Я разместил игровую демонстрацию проблемы здесь, в Google appengine .
Мне было бы очень интересно, сможет ли кто-нибудь побить целевой показатель, поскольку это покажет, что мой текущий алгоритм не оптимален. (Если вам это удастся, следует распечатать поздравительное сообщение!)
Проблема
Мой текущий алгоритм очень плохо масштабируется с количеством целей. Время растет экспоненциально и для 16 рыбок уже несколько секунд.
Я хотел бы вычислить ответ для доски размером 32 * 32 и со 100 движущимися мишенями.
Вопрос
Каков эффективный алгоритм (в идеале на Javascript) для вычисления минимального количества шагов для сбора всех целей?
Что я пробовал
Мой нынешний подход основан на воспоминаниях, но он очень медленный, и я не знаю, всегда ли он будет давать лучшее решение.
Я решаю подзадачу «каково минимальное количество шагов, чтобы собрать данный набор целей и попасть в конкретную цель?».
Подзадача решается рекурсивно, проверяя каждый выбор предыдущей цели, которую нужно посетить. Я предполагаю, что всегда оптимально собрать предыдущее подмножество целей как можно быстрее, а затем как можно быстрее перейти от позиции, в которой вы закончили, к текущей цели (хотя я не знаю, является ли это допустимым предположением).
Это приводит к вычислению 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-сложна и, следовательно, неразумно искать для нее оптимальный ответ.