Latar Belakang
Gambar ini mengilustrasikan masalahnya:
Saya bisa mengontrol lingkaran merah. Targetnya adalah segitiga biru. Panah hitam menunjukkan arah pergerakan target.
Saya ingin mengumpulkan semua target dalam jumlah langkah minimum.
Setiap belokan saya harus bergerak 1 langkah ke kiri / kanan / atas atau bawah.
Setiap belokan target juga akan bergerak 1 langkah sesuai dengan petunjuk yang tertera di papan.
Demo
Saya telah membuat demo yang bisa dimainkan dari masalah ini di sini di Google appengine .
Saya akan sangat tertarik jika ada yang bisa mengalahkan skor target karena ini akan menunjukkan bahwa algoritme saya saat ini kurang optimal. (Pesan selamat harus dicetak jika Anda mengatur ini!)
Masalah
Algoritme saya saat ini berskala sangat buruk dengan jumlah target. Waktu bertambah secara eksponensial dan untuk 16 ikan sudah beberapa detik.
Saya ingin menghitung jawaban untuk ukuran papan 32 * 32 dan dengan 100 target bergerak.
Pertanyaan
Apa algoritme yang efisien (idealnya dalam Javascript) untuk menghitung jumlah langkah minimum untuk mengumpulkan semua target?
Apa yang saya coba
Pendekatan saya saat ini didasarkan pada memoisation tetapi sangat lambat dan saya tidak tahu apakah itu akan selalu menghasilkan solusi terbaik.
Saya memecahkan sub-masalah "berapa jumlah minimum langkah untuk mengumpulkan serangkaian target tertentu dan berakhir pada target tertentu?".
Masalah ini diselesaikan secara rekursif dengan memeriksa setiap pilihan untuk target yang dikunjungi sebelumnya. Saya berasumsi bahwa itu selalu optimal untuk mengumpulkan subkumpulan target sebelumnya secepat mungkin dan kemudian berpindah dari posisi yang Anda selesaikan ke target saat ini secepat mungkin (walaupun saya tidak tahu apakah ini asumsi yang valid).
Ini menghasilkan n * 2 ^ n status yang akan dihitung yang tumbuh dengan sangat cepat.
Kode saat ini ditunjukkan di bawah ini:
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;
}
Apa yang saya pertimbangkan
Beberapa opsi yang membuat saya penasaran adalah:
Caching hasil menengah. Penghitungan jarak mengulangi banyak simulasi dan hasil antara dapat disimpan dalam cache.
Namun, saya tidak berpikir ini akan menghentikannya memiliki kompleksitas eksponensial.Algoritme pencarian A * meskipun tidak jelas bagi saya heuristik yang dapat diterima yang sesuai dan seberapa efektif ini dalam praktiknya.
Menyelidiki algoritme yang baik untuk masalah penjual keliling dan melihat apakah algoritme tersebut berlaku untuk masalah ini.
Mencoba membuktikan bahwa masalahnya NP-hard dan karenanya tidak masuk akal untuk mencari jawaban yang optimal untuk itu.
sumber
Jawaban:
Sudahkah Anda mencari literatur? Saya menemukan makalah ini yang tampaknya menganalisis masalah Anda:
"Melacak target yang bergerak dan masalah penjual keliling yang tidak bergerak": http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.85.9940
"Masalah penjual keliling target bergerak": http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.57.6403
UPDATE 1:
Dua makalah di atas tampaknya berkonsentrasi pada gerakan linier untuk metrik euclidian.
sumber
Metode serakah
Salah satu pendekatan yang disarankan dalam komentar adalah pergi ke target terdekat terlebih dahulu.
Saya telah menyiapkan versi demo yang mencakup biaya yang dihitung melalui metode serakah di sini .
Kodenya adalah:
function greedyMethod(start_x,start_y) { var still_to_visit = (1<<pts.length)-1; var pt=[start_x,start_y]; var s=0; while (still_to_visit) { var besti=-1; var bestc=0; for(i=0;i<pts.length;i++) { var bit = 1<<i; if (still_to_visit&bit) { c = fastest_route(pt,getPt(i,s)); if (besti<0 || c<bestc) { besti = i; bestc = c; } } } s+=c; still_to_visit -= 1<<besti; pt=getPt(besti,s); } return s; }
Untuk 10 target jaraknya sekitar dua kali jarak optimal, tetapi terkadang jauh lebih banyak (mis. * 4) dan terkadang bahkan mencapai jarak optimal.
Pendekatan ini sangat efisien sehingga saya dapat membeli beberapa siklus untuk meningkatkan jawabannya.
Selanjutnya saya mempertimbangkan untuk menggunakan metode koloni semut untuk melihat apakah mereka dapat menjelajahi ruang solusi secara efektif.
Metode koloni semut
Sebuah metode koloni semut tampaknya bekerja luar biasa baik untuk masalah ini. Tautan dalam jawaban ini sekarang membandingkan hasil saat menggunakan metode koloni semut dan serakah.
Idenya adalah bahwa semut memilih rute mereka secara probabilistik berdasarkan tingkat feromon saat ini. Setelah setiap 10 percobaan, kami menyetorkan feromon tambahan di sepanjang jalur terpendek yang mereka temukan.
function antMethod(start_x,start_y) { // First establish a baseline based on greedy var L = greedyMethod(start_x,start_y); var n = pts.length; var m = 10; // number of ants var numrepeats = 100; var alpha = 0.1; var q = 0.9; var t0 = 1/(n*L); pheromone=new Array(n+1); // entry n used for starting position for(i=0;i<=n;i++) { pheromone[i] = new Array(n); for(j=0;j<n;j++) pheromone[i][j] = t0; } h = new Array(n); overallBest=10000000; for(repeat=0;repeat<numrepeats;repeat++) { for(ant=0;ant<m;ant++) { route = new Array(n); var still_to_visit = (1<<n)-1; var pt=[start_x,start_y]; var s=0; var last=n; var step=0; while (still_to_visit) { var besti=-1; var bestc=0; var totalh=0; for(i=0;i<pts.length;i++) { var bit = 1<<i; if (still_to_visit&bit) { c = pheromone[last][i]/(1+fastest_route(pt,getPt(i,s))); h[i] = c; totalh += h[i]; if (besti<0 || c>bestc) { besti = i; bestc = c; } } } if (Math.random()>0.9) { thresh = totalh*Math.random(); for(i=0;i<pts.length;i++) { var bit = 1<<i; if (still_to_visit&bit) { thresh -= h[i]; if (thresh<0) { besti=i; break; } } } } s += fastest_route(pt,getPt(besti,s)); still_to_visit -= 1<<besti; pt=getPt(besti,s); route[step]=besti; step++; pheromone[last][besti] = (1-alpha) * pheromone[last][besti] + alpha*t0; last = besti; } if (ant==0 || s<bestantscore) { bestroute=route; bestantscore = s; } } last = n; var d = 1/(1+bestantscore); for(i=0;i<n;i++) { var besti = bestroute[i]; pheromone[last][besti] = (1-alpha) * pheromone[last][besti] + alpha*d; last = besti; } overallBest = Math.min(overallBest,bestantscore); } return overallBest; }
Hasil
Metode koloni semut ini menggunakan 100 pengulangan 10 semut masih sangat cepat (37ms untuk 16 target dibandingkan dengan 3700ms untuk pencarian menyeluruh) dan nampaknya sangat akurat.
Tabel di bawah ini menunjukkan hasil untuk 10 percobaan dengan menggunakan 16 target:
Greedy Ant Optimal 46 29 29 91 38 37 103 30 30 86 29 29 75 26 22 182 38 36 120 31 28 106 38 30 93 30 30 129 39 38
Metode semut tampaknya jauh lebih baik daripada rakus dan seringkali sangat mendekati optimal.
sumber
Masalahnya mungkin direpresentasikan dalam istilah Masalah Penjual Perjalanan Umum, dan kemudian diubah menjadi Masalah Penjual Perjalanan konvensional. Ini adalah masalah yang dipelajari dengan baik. Ada kemungkinan bahwa solusi paling efisien untuk masalah OP tidak lebih efisien daripada solusi untuk TSP, tetapi tidak pasti (saya mungkin gagal memanfaatkan beberapa aspek dari struktur masalah OP yang akan memungkinkan solusi yang lebih cepat , seperti sifat siklusnya). Bagaimanapun, ini adalah titik awal yang baik.
Dari C. Noon & J. Bean, Transformasi Efisien dari Masalah Penjual Bepergian Umum :
Untuk masalah OP:
N
adalah lokasi ikan tertentu pada waktu tertentu. Mewakili ini sebagai(x, y, t)
, di mana(x, y)
adalah koordinat kisi, dant
merupakan waktu di mana ikan akan berada di koordinat ini. Untuk ikan paling kiri dalam contoh OP, beberapa ikan pertama (berbasis 1) adalah:(3, 9, 1), (4, 9, 2), (5, 9, 3)
saat ikan bergerak ke kanan.fish(n_i)
mengembalikan ID ikan yang diwakili oleh node. Untuk setiap dua anggota N kita dapat menghitungmanhattan(n_i, n_j)
jarak manhattan antara dua node, dantime(n_i, n_j
) untuk waktu offset antar node.S_i
hanya akan terdiri dari node yang untuknyafish(n) == i
.i
danj
fish(n_i) != fish(n_j)
kemudian ada busur antarai
danj
.time(n_i, n_j)
, atau tidak ditentukan jikatime(n_i, n_j) < distance(n_i, n_j)
(yaitu lokasi tidak dapat dicapai sebelum ikan sampai di sana, mungkin karena waktunya mundur). Busur jenis terakhir ini dapat dihilangkan.Memecahkan masalah ini kemudian akan menghasilkan satu kunjungan ke setiap subset node (yaitu setiap ikan diperoleh satu kali) untuk jalur dengan biaya minimal (yaitu waktu minimal untuk mendapatkan semua ikan).
Makalah ini selanjutnya menjelaskan bagaimana rumusan di atas dapat diubah menjadi Masalah Penjual Perjalanan tradisional dan kemudian diselesaikan atau diperkirakan dengan teknik yang ada. Saya belum membaca detailnya tetapi makalah lain yang melakukan ini dengan cara yang diklaim efisien adalah yang ini .
Ada masalah yang jelas dengan kompleksitas. Secara khusus, ruang node tidak terbatas! Ini dapat diatasi dengan hanya menghasilkan node hingga jangka waktu tertentu. Jika
t
jumlah langkah waktu untuk menghasilkan node danf
jumlah ikan maka ukuran ruang node akan menjadit * f
. Node pada suatu waktuj
akan memiliki paling banyak(f - 1) * (t - j)
busur keluar (karena tidak dapat mundur ke masa lalu atau ke subsetnya sendiri). Jumlah total busur akan berada dalam urutant^2 * f^2
busur. Struktur busur mungkin dapat dirapikan, untuk memanfaatkan fakta bahwa jalur ikan pada akhirnya bersifat siklis. Ikan akan mengulangi konfigurasi mereka sekali setiap penyebut umum terendah dari panjang siklusnya jadi mungkin fakta ini dapat digunakan.Saya tidak cukup tahu tentang TSP untuk mengatakan apakah ini layak atau tidak, dan menurut saya itu tidak berarti bahwa masalah yang diposting pasti NP-hard ... tetapi ini adalah salah satu pendekatan untuk menemukan solusi yang optimal atau terbatas .
sumber
Saya pikir pendekatan lain adalah:
Kutipan wikipedia:
Dalam matematika, diagram voronoi adalah cara membagi ruang menjadi beberapa daerah. Satu set titik (disebut benih, lokasi, atau generator) ditentukan sebelumnya dan untuk setiap benih akan ada wilayah terkait yang terdiri dari semua titik yang lebih dekat ke benih itu daripada yang lain.
Jadi, Anda memilih target, ikuti jalurnya untuk beberapa langkah dan tetapkan titik awal di sana. Lakukan ini dengan semua target lainnya juga dan Anda mendapatkan diagram voroni. Bergantung di area mana Anda berada, Anda pindah ke titik benih itu. Viola, kamu mendapat ikan pertama. Sekarang ulangi langkah ini sampai Anda mendapatkan semuanya.
sumber