Làm cách nào để tìm đường đi ngắn nhất giữa 100 mục tiêu di động? (Bao gồm bản demo trực tiếp.)


89

Lý lịch

Hình ảnh này minh họa vấn đề: square_grid_with_arrows_giving_directions

Tôi có thể kiểm soát vòng tròn màu đỏ. Mục tiêu là các hình tam giác màu xanh lam. Các mũi tên màu đen cho biết hướng mà các mục tiêu sẽ di chuyển.

Tôi muốn thu thập tất cả các mục tiêu theo số bước tối thiểu.

Mỗi lượt tôi phải di chuyển 1 bước sang trái / phải / lên hoặc xuống.

Mỗi lượt đi các mục tiêu cũng sẽ di chuyển 1 bước theo chỉ dẫn trên bảng.

Bản giới thiệu

Tôi đã đưa ra một bản demo có thể chơi được về vấn đề ở đây trên Google appengine .

Tôi sẽ rất quan tâm nếu ai đó có thể vượt qua điểm số mục tiêu vì điều này sẽ cho thấy rằng thuật toán hiện tại của tôi là chưa tối ưu. (Một thông báo chúc mừng sẽ được in nếu bạn quản lý được điều này!)

Vấn đề

Thuật toán hiện tại của tôi có quy mô thực sự tồi tệ với số lượng mục tiêu. Thời gian tăng lên theo cấp số nhân và đối với 16 con cá là vài giây.

Tôi muốn tính toán câu trả lời cho kích thước bảng là 32 * 32 và với 100 mục tiêu di động.

Câu hỏi

Thuật toán hiệu quả (lý tưởng là trong Javascript) để tính toán số bước tối thiểu để thu thập tất cả các mục tiêu là gì?

Những gì tôi đã thử

Cách tiếp cận hiện tại của tôi dựa trên sự ghi nhớ nhưng nó rất chậm và tôi không biết liệu nó có luôn tạo ra giải pháp tốt nhất hay không.

Tôi giải quyết vấn đề con "số bước tối thiểu để thu thập một tập hợp mục tiêu nhất định và kết thúc ở một mục tiêu cụ thể là bao nhiêu?".

Bài toán con được giải một cách đệ quy bằng cách kiểm tra từng lựa chọn cho mục tiêu trước đó đã được truy cập. Tôi giả định rằng luôn luôn là tối ưu để thu thập tập hợp con mục tiêu trước đó càng nhanh càng tốt và sau đó di chuyển từ vị trí bạn đã kết thúc đến mục tiêu hiện tại càng nhanh càng tốt (mặc dù tôi không biết liệu đây có phải là một giả định hợp lệ hay không).

Điều này dẫn đến n * 2 ^ n trạng thái được tính toán phát triển rất nhanh.

Mã hiện tại được hiển thị bên dưới:

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;
}

Những gì tôi đã xem xét

Một số tùy chọn mà tôi băn khoăn là:

  1. Lưu trữ các kết quả trung gian. Việc tính toán khoảng cách lặp lại rất nhiều mô phỏng và kết quả trung gian có thể được lưu vào bộ nhớ đệm.
    Tuy nhiên, tôi không nghĩ rằng điều này sẽ ngăn nó có độ phức tạp theo cấp số nhân.

  2. Một thuật toán tìm kiếm A * mặc dù đối với tôi không rõ ràng là một kinh nghiệm tìm kiếm có thể chấp nhận thích hợp sẽ là gì và điều này sẽ hiệu quả như thế nào trong thực tế.

  3. Điều tra các thuật toán tốt cho bài toán nhân viên bán hàng lưu động và xem chúng có áp dụng cho bài toán này không.

  4. Cố gắng chứng minh rằng vấn đề là NP-khó và do đó không hợp lý khi tìm kiếm một câu trả lời tối ưu cho nó.


1
Tôi sẽ đi cho # 4 và sau đó là # 3: Với các bo mạch đủ lớn, nó bắt chước TSP khá tốt.
John Dvorak

2
Theo như tôi biết, TSP là NP-cứng với số liệu euclidean cũng như số liệu manhattan (lưới vuông).
John Dvorak

1
Nếu bạn làm điều đó bằng cách tìm kiếm trên cây đơn giản, vâng, nó sẽ là cấp số nhân. Tuy nhiên, nếu bạn có thể tìm thấy một kinh nghiệm phù hợp ở mỗi bước, nó có thể không thực sự tối ưu, nhưng nó có thể rất tốt. Một kinh nghiệm có thể xảy ra là, nhìn vào tập hợp cá hiện tại, con nào có thể tiếp cận nhanh nhất? Một kinh nghiệm thứ hai có thể là, tôi có thể tiếp cận 2 con cá nào nhanh nhất?
Mike Dunlavey

2
@MikeDunlavey sẽ tương ứng với thuật toán TSP tham lam và nó hoạt động rất tốt trong thực tế. Đi cho cá gần nhất có vẻ như là một ý tưởng tốt
John Dvorak

1
+1 cho một trong những câu hỏi hay nhất mà tôi thấy gần đây, cả về nội dung và cấu trúc.
lướt web vào

Câu trả lời:


24

Bạn đã tìm kiếm tài liệu chưa? Tôi tìm thấy những bài báo này dường như phân tích vấn đề của bạn:

CẬP NHẬT 1:

Hai bài báo trên dường như tập trung vào chuyển động tuyến tính của hệ mét euclid.


Cảm ơn - Tôi đã không nhìn thấy những giấy tờ đó nhưng chúng trông rất phù hợp. Tôi sẽ xem liệu tôi có thể điều chỉnh thuật toán di truyền để hoạt động trong trường hợp của tôi hay không và so sánh nó với kết quả từ phương pháp vũ phu.
Peter de Rivaz,

13

Phương pháp tham lam

Một cách tiếp cận được đề xuất trong các nhận xét là đi đến mục tiêu gần nhất trước tiên.

Tôi đã đưa ra một phiên bản của bản demo bao gồm chi phí được tính bằng phương pháp tham lam này ở đây .

Mã là:

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;
}

Đối với 10 mục tiêu, khoảng cách đó là khoảng gấp đôi khoảng cách tối ưu, nhưng đôi khi hơn nhiều (ví dụ: * 4) và đôi khi còn đạt khoảng cách tối ưu.

Cách tiếp cận này rất hiệu quả nên tôi có thể dành một số chu kỳ để cải thiện câu trả lời.

Tiếp theo, tôi đang xem xét sử dụng phương pháp lập đàn kiến ​​để xem liệu chúng có thể khám phá không gian giải pháp một cách hiệu quả hay không.

Phương pháp đàn kiến

Một phương pháp thuộc địa kiến dường như hoạt động tốt cho vấn đề này. Liên kết trong câu trả lời này bây giờ so sánh kết quả khi sử dụng cả phương pháp tham lam và đàn kiến.

Ý tưởng là kiến ​​chọn đường đi của chúng một cách xác suất dựa trên mức pheromone hiện tại. Sau mỗi 10 lần thử nghiệm, chúng tôi gửi thêm pheromone theo con đường ngắn nhất mà họ tìm thấy.

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;
}

Các kết quả

Phương pháp lập đàn kiến ​​này sử dụng 100 lần lặp lại của 10 con kiến ​​vẫn rất nhanh (37ms cho 16 mục tiêu so với 3700ms cho tìm kiếm toàn diện) và có vẻ rất chính xác.

Bảng dưới đây cho thấy kết quả của 10 thử nghiệm sử dụng 16 mục tiêu:

   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

Phương pháp con kiến ​​có vẻ tốt hơn đáng kể so với phương pháp tham lam và thường rất gần với mức tối ưu.


Đẹp. Bạn có thể chưa có kết quả tối ưu từ việc tìm kiếm đầy đủ (hoặc có thể không bao giờ do tính khó hiểu của nó!) Nhưng sẽ rất thú vị khi thấy cách đàn kiến ​​quy mô với kích thước bảng (32x32) với cùng số lượng mục tiêu.
timxyz

8

Vấn đề có thể được trình bày dưới dạng Bài toán nhân viên bán hàng đi du lịch tổng quát, và sau đó được chuyển thành Bài toán nhân viên bán hàng đi du lịch thông thường. Đây là một vấn đề được nghiên cứu kỹ lưỡng. Có thể các giải pháp hiệu quả nhất cho vấn đề của OP không hiệu quả hơn các giải pháp cho TSP, nhưng không có nghĩa là chắc chắn (tôi có thể không tận dụng được một số khía cạnh trong cấu trúc vấn đề của OP để cho phép đưa ra giải pháp nhanh hơn , chẳng hạn như tính chất chu kỳ của nó). Dù bằng cách nào, đó là một điểm khởi đầu tốt.

Từ C. Noon & J.Bean, Một sự chuyển đổi hiệu quả của vấn đề người bán hàng đi du lịch tổng quát :

Các Generalized Salesman Du lịch Vấn đề (GTSP) là một mô hình hữu ích cho các vấn đề liên quan đến quyết định lựa chọn và trình tự. Phiên bản không đối xứng của bài toán được xác định trên một đồ thị có hướng với các nút N, nối các cung A và một véc tơ của cung tương ứng c. Các nút được nhóm lại thành m tập hợp nút loại trừ lẫn nhau và toàn bộ. Các cung kết nối chỉ được xác định giữa các nút thuộc các tập hợp khác nhau, nghĩa là không có các cung intraset. Mỗi cung được xác định có một chi phí không âm tương ứng. GTSP có thể được phát biểu như một bài toán tìm kiếm một chu kỳ m-cung với chi phí tối thiểu bao gồm chính xác một nút từ mỗi tập hợp nút .

Đối với vấn đề của OP:

  • Mỗi thành viên Nlà một vị trí của cá cụ thể tại một thời điểm cụ thể. Biểu diễn điều này dưới dạng (x, y, t), đâu (x, y)là một tọa độ lưới và tlà thời gian mà cá sẽ ở tọa độ này. Đối với con cá ngoài cùng bên trái trong ví dụ của OP, một số đầu tiên trong số này (dựa trên 1) là: (3, 9, 1), (4, 9, 2), (5, 9, 3)khi con cá di chuyển sang phải.
  • Đối với bất kỳ thành viên nào của N, hãy fish(n_i)trả về ID của cá được đại diện bởi nút. Đối với bất kỳ hai thành viên nào của N, chúng ta có thể tính toán manhattan(n_i, n_j)khoảng cách manhattan giữa hai nút và time(n_i, n_j) cho khoảng thời gian giữa các nút.
  • Số tập con rời rạc m bằng số cá. Tập hợp con rời rạc S_isẽ chỉ bao gồm các nút cho nó fish(n) == i.
  • Nếu cho hai nút ij fish(n_i) != fish(n_j)sau đó có một cung giữa ij.
  • Chi phí giữa nút i và nút j là time(n_i, n_j), hoặc không xác định nếu time(n_i, n_j) < distance(n_i, n_j)(nghĩa là không thể đến được vị trí trước khi cá đến đó, có lẽ vì nó quay ngược thời gian). Các cung của loại sau này có thể được loại bỏ.
  • Một nút bổ sung sẽ cần được thêm đại diện cho vị trí của người chơi với các vòng cung và chi phí cho tất cả các nút khác.

Giải quyết vấn đề này sau đó sẽ dẫn đến một lần truy cập vào mỗi tập con nút (tức là mỗi con cá được lấy một lần) cho một con đường với chi phí tối thiểu (tức là thời gian tối thiểu để thu được tất cả cá).

Bài báo tiếp tục mô tả cách công thức trên có thể được chuyển đổi thành Bài toán người bán hàng đi du lịch truyền thống và sau đó được giải quyết hoặc gần đúng với các kỹ thuật hiện có. Tôi chưa đọc qua chi tiết nhưng một bài báo khác thực hiện điều này theo cách mà nó tuyên bố là hiệu quả là bài báo này .

Có những vấn đề rõ ràng với sự phức tạp. Đặc biệt, không gian nút là vô hạn! Điều này có thể được giảm bớt bằng cách chỉ tạo ra các nút trong một khoảng thời gian nhất định. Nếu tlà số bước thời gian để tạo nút và flà số cá thì kích thước của không gian nút sẽ là t * f. Một nút tại một thời điểm jsẽ có nhiều nhất (f - 1) * (t - j)cung đi ra (vì nó không thể di chuyển ngược thời gian hoặc về tập con của chính nó). Tổng số cung sẽ theo thứ tự của t^2 * f^2cung. Cấu trúc vòng cung có thể được sắp xếp gọn gàng hơn, để tận dụng lợi thế của thực tế là các đường đi của cá cuối cùng là theo chu kỳ. Cá sẽ lặp lại cấu hình của chúng một lần sau mỗi mẫu số chung thấp nhất của độ dài chu kỳ của chúng, vì vậy có lẽ thực tế này có thể được sử dụng.

Tôi không biết đủ về TSP để nói liệu điều này có khả thi hay không và tôi không nghĩ rằng điều đó có nghĩa là vấn đề được đăng nhất thiết phải là NP-khó ... nhưng đó là một cách tiếp cận để tìm ra giải pháp tối ưu hoặc có giới hạn .


Cảm ơn, điều này là mới đối với tôi và rất thú vị. Tôi nghĩ rằng tôi sẽ có thể sử dụng phép biến đổi này kết hợp với thuật toán Christofides để tìm ra giải pháp một cách hiệu quả trong phạm vi hệ số xấp xỉ 3/2 của mức tối ưu. Nếu tôi làm cho nó hoạt động, tôi sẽ thêm các tuyến đường được sản xuất vào trang demo.
Peter de Rivaz,

Ah, tôi nghĩ rằng có một vấn đề với kế hoạch của tôi ở chỗ mặc dù bài toán ban đầu của tôi là một đồ thị hoàn chỉnh thỏa mãn một bất đẳng thức thích hợp trên số liệu, nhưng phép biến đổi được mô tả dẫn đến một đồ thị không đầy đủ và do đó thuật toán Christofides không còn được áp dụng nữa. Cảm ơn dù sao cho quan điểm thú vị.
Peter de Rivaz,

Vâng, tôi quên đề cập rằng bất đẳng thức tam giác không còn được giữ nữa. Tuy nhiên, đó là một điểm khởi đầu tốt cho các giải pháp heuristic và các phép gần đúng tổng quát hơn.
timxyz

1

Tôi nghĩ một cách tiếp cận khác sẽ là:

  • tính toán đường đi của các mục tiêu - dự đoán.
  • hơn là sử dụng sơ đồ Voronoi

Trích wikipedia:

Trong toán học, biểu đồ Voronoi là một cách phân chia không gian thành một số vùng. Một tập hợp các điểm (được gọi là hạt giống, vị trí hoặc bộ tạo) được chỉ định trước và đối với mỗi hạt giống sẽ có một vùng tương ứng bao gồm tất cả các điểm gần hạt giống đó hơn bất kỳ vùng nào khác.

Vì vậy, bạn chọn một mục tiêu, đi theo con đường của nó trong một số bước và đặt điểm khởi đầu ở đó. Làm điều này với tất cả các mục tiêu khác và bạn sẽ có được một sơ đồ voroni. Tùy thuộc vào khu vực bạn đang ở, bạn di chuyển đến điểm khởi đầu của nó. Viola, bạn đã có con cá đầu tiên. Bây giờ lặp lại bước này cho đến khi bạn hoàn thành tất cả.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.