Làm thế nào để tìm ra con đường ngắn nhất với các nút wormhole?


25

thí dụ

Đây là một ví dụ về những gì tôi muốn làm thông qua mã. Tôi biết bạn có thể sử dụng tìm kiếm điểm nhảy để dễ dàng chuyển từ nút xanh sang nút đỏ mà không gặp vấn đề gì, hoặc thậm chí là A *. Nhưng làm thế nào để bạn tính toán điều này với warps.

Trong ảnh, bạn có thể thấy rằng chỉ mất 8 lần di chuyển để chuyển từ nút xanh sang nút đỏ khi đi theo đường dẫn màu xanh. Đường dẫn màu xanh ngay lập tức di chuyển vị trí của bạn từ một nút màu tím sang nút tiếp theo. Khoảng trống ở giữa có giá 2 lần di chuyển là một điểm giữa hai vùng dọc mà bạn phải di chuyển để đến.

Rõ ràng là nhanh hơn để đi theo con đường màu xanh, vì bạn chỉ cần di chuyển một nửa (gần như) đến con đường màu vàng, nhưng làm thế nào để tôi làm điều này theo lập trình?

Với mục đích giải quyết vấn đề này, hãy giả sử rằng có nhiều "vệt" màu tím xung quanh biểu đồ mà bạn có thể sử dụng, VÀ chúng tôi biết chính xác từng điểm màu tím sẽ di chuyển đến đâu và vị trí của chúng trên biểu đồ.

Một số sợi dọc màu tím là hai hướng, và một số thì không, có nghĩa là, đôi khi bạn chỉ có thể nhập một sợi dọc từ một phía, nhưng không quay trở lại sau khi cong vênh.

Tôi đã nghĩ về giải pháp và chỉ kết luận rằng tôi sẽ có thể tính toán được vấn đề bằng cách kiểm tra khoảng cách đến từng điểm dọc (trừ các điểm đơn hướng) và sự khác biệt giữa các điểm đó và các điểm gần với chúng .

Chương trình sẽ phải tìm ra bằng cách nào đó có lợi hơn khi thực hiện lần thứ hai, thay vì đi bộ từ lần nhảy đầu tiên. Vì vậy, thay vì di chuyển 6 điểm, sau đó cong vênh, sau đó di chuyển 8 bước còn lại bằng chân (cũng nhanh hơn so với việc không sử dụng warp nào), nó sẽ thực hiện 6 bước, sau đó hai bước chuyển sang warp thứ hai.

EDIT: Tôi nhận ra con đường màu xanh thực sự sẽ thực hiện 12 bước, thay vì 8, nhưng câu hỏi vẫn giữ nguyên.


4
Không phải con đường màu xanh là 12 (bao gồm cả hai để đi từ màu tím cuối cùng sang màu đỏ)?
BlueRaja - Daniel Pflughoeft

5
Màu xanh thực sự là 12 (7 + 3 + 2) di chuyển, không?
Daniel Jour

Rất tiếc, rối tung lên, cảm ơn các bạn! @DanielJour và Blue
Jeff smith

Cách "chính xác" để mô hình hóa khoảng cách sẽ là sử dụng cấu trúc liên kết và mô hình hóa này như một bề mặt chiều cao hơn. Tôi tự hỏi nếu một câu trả lời như vậy sẽ thích hợp ở đây?
Geeky tôi

Câu trả lời:


49

Hầu hết các thuật toán tìm đường được xác định theo biểu đồ, không phải về mặt lưới. Trong một biểu đồ, một kết nối giữa hai nút ở xa không thực sự là một vấn đề.

Tuy nhiên, bạn phải chăm sóc với heuristic của bạn. Với các lỗ sâu đục, khoảng cách tối thiểu giữa hai nút không còn là khoảng cách euclide và khoảng cách không thỏa mãn bất đẳng thức tam giác. Heuristic như vậy là không thể chấp nhận cho A *. Do đó, bạn không thể sử dụng A * một cách dễ dàng.

Tất nhiên các thuật toán tìm đường dẫn như Dijkstra không sử dụng heuristic vẫn sẽ hoạt động. Điều này giống như một tìm kiếm đầu tiên theo chiều rộng và sẽ chọn lỗ sâu của bạn mà không cần nỗ lực thêm. Tuy nhiên, Dijkstra sẽ truy cập vào nhiều nút mà A * có khả năng heuristic tốt. (Dijkstra tương đương với A * với heuristic(x) = 0.)

Tôi nghĩ rằng A * sẽ hoạt động nếu bạn sử dụng một heuristic coi tất cả các lỗ sâu đục ra như một lỗ sâu đục trực tiếp đến mục tiêu: heuristic có thể đánh giá thấp khoảng cách, nhưng không bao giờ được đánh giá quá cao. Tức là heuristic sẽ là:

def wormhole_heuristic(x):
  return min(euclidean_distance(x, g) for g in [goal, wormholes...])

Đối với một heuristic rất chính xác, bạn có thể (đệ quy) thêm khoảng cách từ điểm cuối của lỗ sâu đến mục tiêu hoặc lỗ sâu tiếp theo. Tức là như một tính toán trước, bạn có thể thực hiện tìm đường dẫn trên sơ đồ con (hoàn toàn được kết nối) có chứa tất cả các lỗ sâu và mục tiêu, trong đó khoảng cách giữa hai nút là khoảng cách euclide của chúng. Điều này có thể có lợi nếu số lượng lỗ sâu ít hơn nhiều so với số lượng ô có thể truy cập trên lưới của bạn. Các heuristic mới sẽ là:

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  via_wormhole = min(euclidean_distance(x, w) + wormhole_path_distance(w, goal) for w in wormholes)
  return min(direct, via_wormhole)

Như @Caleth đã chỉ ra trong các bình luận, đây là tất cả rất có thể điều chỉnh được và chúng ta có thể cải thiện heuristic wormhole đầu tiên mà không cần thực hiện một đường dẫn đầy đủ thông qua mạng wormhole, bằng cách thêm khoảng cách giữa lối thoát worm worm cuối cùng và mục tiêu. Bởi vì chúng tôi không biết lối thoát worm worm nào sẽ được sử dụng sau cùng và chúng tôi không được đánh giá quá cao, chúng tôi phải giả định lối thoát gần nhất với mục tiêu:

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  to_next_wormhole = min(euclidean_distance(x, w) for w in wormholes)
  from_last_wormhole = min(euclidean_distance(w.exit, goal) for w in wormholes)
  via_wormhole = to_next_wormhole + from_last_wormhole
  return min(direct, via_wormhole)

10
Đáng chú ý là Dijkstra chỉ là A * vớidijkstra_heuristic(x) = 0
Caleth

Tôi không hiểu ý của bạn về [* lỗ sâu đục, mục tiêu], bạn sẽ giải thích điều này chứ?
Jeff smith

1
"Khoảng cách Euclide đến lối ra lỗ sâu gần nhất" là một ước tính rẻ wormhole_path_distancehơn so với tìm kiếm sơ đồ con và ít đánh giá thấp hơn "tất cả các lối thoát đều nằm trên mục tiêu".
Caleth

3
@Caleth chắc chắn! Có rất nhiều tiềm năng tinh chỉnh ở đây, ví dụ chúng ta có thể quyết định nhìn về phía trước n = 3 lần nhảy. Việc tìm kiếm sơ đồ con tương ứng với việc đóng tất cả các bước nhảy lỗ sâu đục. Gợi ý của bạn để nhìn về phía trước n = 1 nhảy là rất thanh lịch vì nó có về cơ bản thêm zero chi phí :)
amon

1
Để đơn giản hóa, giả sử chỉ có một lỗ giun (hai nút), sau đó bạn có thể chuyển đổi mặt phẳng 1 lỗ giun này thành 2 mặt phẳng gương, bằng cách sao chép một mặt phẳng đối xứng với đường thẳng cách nhau giữa hai điểm này dưới dạng trục đối xứng. Bây giờ bạn có hai mặt phẳng, hãy gọi chúng là mặt phẳng thực (bạn không lấy lỗ giun) và mặt phẳng tưởng tượng (bạn lấy lỗ giun). Bây giờ, chúng tôi giới thiệu tọa độ Z. Tọa độ này sẽ là 0 cho mọi điểm trong mặt phẳng thực và nó sẽ là dist (lỗ sâu, điểm) cho mọi điểm của mặt phẳng tưởng tượng. Sau đó, áp dụng A * cho không gian 3 chiều.
lilezek

5

Bạn có một biểu đồ với 6 đỉnh trên lưới có tọa độ:

A ( 0,0)
B ( 4,7)
C ( 7,4)
D (10,4)
E (16,2)
F (16,0)

Bạn có thể tạo một biểu đồ hoàn chỉnh trên các đỉnh đó và gán chi phí cho mỗi cạnh trong đó chi phí MAX( ABS( x1 - x2 ), ABS( y1 - y2 ) )dành cho các cạnh tiêu chuẩn và chi phí là 0 cho các lỗ sâu đục.

Điều này sẽ cung cấp cho bạn các chi phí (dưới dạng ma trận kề):

   A  B  C  D  E  F
- -- -- -- -- -- --
A  -  7  7 10 16 16
B  7  -  0  6 12 12
C  7  0  -  3  9  9
D 10  6  3  -  0  6
E 16 12  9  0  -  2
F 16 12  9  6  2  -

Nếu có các sợi dọc một hướng thì chỉ tạo các cạnh trong đồ thị (hoặc ma trận kề) đi theo hướng đó chứ không phải theo hướng ngược lại.

Sau đó, bạn có thể sử dụng thuật toán của Dijkstra với hàng đợi ưu tiên .

Bắt đầu từ Avà đẩy từng cạnh liền kề vào hàng ưu tiên:

Định dạng: (đường dẫn: chi phí)

queue     = [ (A-B : 7), (A-C : 7), (A-D : 10), (A-E : 16), (A-F : 16) ]

Vì các mục được đẩy lên hàng đợi - theo dõi chi phí tối thiểu cho mỗi đỉnh và chỉ đẩy các đường dẫn lên hàng đợi nếu chi phí thấp hơn chi phí tối thiểu hiện có.

min-costs = { A: 0, B: 7, C: 7, D: 10, E: 16, F: 16 }

Xóa mục đầu tiên khỏi hàng đợi và, nếu chi phí của nó vẫn khớp với chi phí tối thiểu, hãy đẩy tất cả các đường dẫn tổng hợp được tạo bởi đường dẫn đó và các cạnh liền kề của nó trở lại hàng đợi ưu tiên (nếu các đường dẫn hỗn hợp có chi phí thấp hơn mức tối thiểu hiện có):

Tẩy: (A-B : 7)

  • Thử (A-B-A : 14)- từ chối vì chi phí cao hơn
  • Thử (A-B-C : 7)- từ chối cùng một chi phí
  • Thử (A-B-D : 13)- từ chối vì chi phí cao hơn
  • Thử (A-B-E : 19)- từ chối vì chi phí cao hơn
  • Thử (A-B-F : 19)- từ chối vì chi phí cao hơn

Tẩy (A-C : 7)

  • Thử (A-C-A : 14)- từ chối vì chi phí cao hơn
  • Thử (A-C-B : 7)- từ chối cùng một chi phí
  • Thử (A-C-D : 10)- từ chối cùng một chi phí
  • Thử (A-C-E : 16)- từ chối cùng một chi phí
  • Thử (A-C-F : 16)- từ chối cùng một chi phí

Tẩy (A-D : 10)

  • Thử (A-D-A : 20)- từ chối vì chi phí cao hơn
  • Thử (A-D-B : 16)- từ chối vì chi phí cao hơn
  • Thử (A-D-C : 13)- từ chối vì chi phí cao hơn
  • Hãy thử (A-D-E : 10)- chèn vào hàng đợi
  • Thử (A-D-F : 16)- từ chối cùng một chi phí

Bây giờ hàng đợi sẽ trông như sau:

queue     = [ (A-D-E : 10), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 16 }

Tẩy (A-D-E : 10)

  • Thử (A-D-E-A : 26)- từ chối vì chi phí cao hơn
  • Thử (A-D-E-B : 22)- từ chối vì chi phí cao hơn
  • Thử (A-D-E-C : 19)- từ chối vì chi phí cao hơn
  • Thử (A-D-E-D : 10)- từ chối cùng một chi phí
  • Hãy thử (A-D-E-F : 12)- chèn vào hàng đợi

Sau đó, hàng đợi là:

queue     = [ (A-D-E-F : 12), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 12 }

Xóa (A-D-E-F : 12), thấy rằng bạn đã đến nút đích với chi phí là 12.

Lưu ý: các đường dẫn (A-B-C-D-E-F), (A-C-D-E-F)(A-D-E-F)tất cả đều có chi phí tối thiểu như nhau là 12.


0

Thiết lập một ma trận chứa tất cả các đỉnh và sử dụng Thuật toán Floyd-Wallenstein hoặc Thuật toán Bellman-Ford. Cả hai sẽ dẫn đến một ma trận với các đường dẫn ngắn nhất có thể giữa tất cả các điểm. Sau đó, bạn có thể lặp qua ma trận để tìm đường đi ngắn nhất nối hai điểm. (vấn đề của bạn giống như một TSP không đối xứng).

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.