จะค้นหาเส้นทางที่สั้นที่สุดที่มีโหนดหนอนได้อย่างไร


25

ตัวอย่าง

นี่คือตัวอย่างของสิ่งที่ฉันต้องการทำผ่านรหัส ฉันรู้ว่าคุณสามารถใช้การค้นหาจุดกระโดดได้อย่างง่ายดายจากโหนดสีเขียวไปยังโหนดสีแดงโดยไม่มีปัญหาหรือแม้แต่ A * แต่คุณจะคำนวณสิ่งนี้อย่างไรกับการแปรปรวน

ในภาพคุณจะเห็นว่ามันใช้เวลาเพียง 8 ย้ายจากโหนดสีเขียวไปยังโหนดสีแดงเมื่อถ่ายเส้นทางสีฟ้า เส้นทางสีน้ำเงินย้ายตำแหน่งของคุณจากโหนดสีม่วงไปยังตำแหน่งถัดไปทันที พื้นที่ตรงกลางที่มีค่าใช้จ่าย 2 การเคลื่อนไหวเป็นจุดระหว่างสองโซน warp ที่คุณต้องย้ายไป

เห็นได้ชัดว่าเร็วกว่าที่จะใช้เส้นทางสีฟ้าเนื่องจากคุณจะต้องย้ายครึ่งหนึ่ง (ประมาณ) เท่า ๆ กับเส้นทางสีเหลือง แต่ฉันจะทำสิ่งนี้อย่างไรในเชิงโปรแกรม?

เพื่อจุดประสงค์ในการแก้ปัญหานี้สมมติว่ามี "วาร์ป" สีม่วงหลายรอบกราฟที่คุณสามารถใช้งานได้และเรารู้ว่าจุดสีม่วงแต่ละจุดจะโค้งงอและตรงไหนบนกราฟ

วาร์ปสีม่วงบางตัวเป็นแบบสองทิศทางและบางอันไม่ใช่ความหมายบางครั้งคุณสามารถป้อนวาร์ปจากด้านหนึ่ง แต่ไม่กลับไปหลังจากการแปรปรวน

ฉันคิดเกี่ยวกับวิธีแก้ปัญหาและสรุปเพียงว่าฉันจะสามารถคำนวณปัญหาได้โดยการตรวจสอบระยะทางไปยังจุดแปรปรวนแต่ละจุด (ลบจุดเดียวทิศทาง) และความแตกต่างระหว่างจุดเหล่านั้นและจุดที่อยู่ใกล้พวกเขา .

โปรแกรมจะต้องคิดออกว่ามันจะมีประโยชน์มากกว่าที่จะใช้วิปริตที่สองแทนที่จะเดินจากการกระโดดครั้งแรก ดังนั้นแทนที่จะเคลื่อนที่ 6 จุดจากนั้นแปรปรวนจากนั้นย้ายส่วนที่เหลืออีก 8 ก้าวด้วยการเดินเท้า (ซึ่งเร็วกว่าการไม่ใช้ warps เลย) จะใช้การเคลื่อนไหว 6 ครั้งจากนั้นทั้งสองเดินไปที่วิปริตสอง

แก้ไข:ฉันรู้ว่าเส้นทางสีน้ำเงินจะใช้เวลา 12 การเคลื่อนไหวแทนที่จะเป็น 8 แต่คำถามยังคงเหมือนเดิม


4
ไม่ควรเส้นทางสีน้ำเงินเป็น 12 (รวมทั้งสองได้จากสีม่วงสุดท้ายไปยังสีแดง)
BlueRaja - Danny Pflughoeft

5
สีน้ำเงินเป็นจริง 12 (7 + 3 + 2) ย้ายไม่?
Daniel Jour

อ๊ะสับสนแล้วขอบคุณมาก! @DanielJour and Blue
Jeff smith

วิธี "ถูกต้อง" เพื่อจำลองระยะทางคือการใช้โทโพโลยีและโมเดลนี้เป็นพื้นผิวมิติที่สูง ฉันสงสัยว่าคำตอบนั้นเหมาะสมหรือไม่
Geeky ฉัน

คำตอบ:


49

อัลกอริธึมการค้นหาเส้นทางส่วนใหญ่ถูกกำหนดไว้ในรูปของกราฟไม่ใช่ในแง่ของกริด ในกราฟการเชื่อมต่อระหว่างสองโหนดอื่นที่อยู่ห่างไกลไม่ใช่ปัญหาจริงๆ

อย่างไรก็ตามคุณต้องดูแลฮิวริสติกของคุณด้วย ด้วย Wormholes ระยะห่างต่ำสุดระหว่างสองโหนดจะไม่เท่ากับระยะยูคลิดและระยะทางจะไม่ตรงกับความไม่เท่าเทียมกันของสามเหลี่ยม ฮิวริสติกแบบนี้ไม่สามารถยอมรับได้สำหรับ A * คุณจึงไม่สามารถใช้ A * ได้อย่างง่ายดาย

เส้นทางที่แน่นอนในการค้นหาอัลกอริทึมเช่น Dijkstra ที่ไม่ใช้การวิเคราะห์พฤติกรรมจะยังคงใช้ได้ นี่เป็นเหมือนการค้นหาแบบกว้าง ๆ และจะเลือกหนอนของคุณโดยไม่ต้องใช้ความพยายามเป็นพิเศษ อย่างไรก็ตาม Dijkstra จะไปเยี่ยมชมโหนดอื่น ๆ ที่ A * พร้อมด้วยฮิวริสติกที่ดี (Dijkstra เทียบเท่ากับ A * ด้วยheuristic(x) = 0)

ฉันคิดว่า A * จะใช้งานได้ถ้าคุณใช้ฮิวริสติกที่จัดการกับหนอนตัวเมียที่ส่งออกทั้งหมดเป็นหนอนโดยตรงไปยังเป้าหมาย: ฮิวริสติกอาจประเมินระยะทางต่ำเกินไป แต่จะต้องไม่ประเมินค่าสูงไป คือฮิวริสติกคือ:

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

สำหรับฮิวริสติกที่แม่นยำมากคุณสามารถเพิ่มระยะทางจากจุดสิ้นสุดของหนอนไปยังเป้าหมายหรือช่องโหว่ถัดไปได้ นั่นคือการคำนวณล่วงหน้าคุณสามารถทำการค้นหาเส้นทางบนกราฟย่อย (เชื่อมต่อทั้งหมด) ที่มีหนอนทั้งหมดและเป้าหมายโดยที่ระยะห่างระหว่างสองโหนดคือระยะทางแบบยูคลิด สิ่งนี้อาจเป็นประโยชน์หากจำนวนตัวหนอนมีจำนวนน้อยกว่าจำนวนเซลล์ที่สามารถเข้าถึงได้ในตารางของคุณ ฮิวริสติกใหม่จะเป็น:

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)

@Caleth ชี้ให้เห็นในความคิดเห็นนี่เป็นสิ่งที่ปรับได้มากและเราสามารถปรับปรุง heuristic หนอนตัวแรกโดยไม่ต้องค้นหาเส้นทางแบบเต็มผ่านเครือข่ายหนอนโดยเพิ่มระยะห่างระหว่างทางออกหนอนและเป้าหมาย เนื่องจากเราไม่ทราบว่าจะใช้ทางออกหนอนใดและจะต้องไม่ประเมินค่าสูงเกินไปเราจึงต้องถือว่าทางออกใกล้เคียงกับเป้าหมายมากที่สุด:

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
เป็นที่น่าสังเกตว่า Dijkstra เป็นเพียง A * ด้วยdijkstra_heuristic(x) = 0
Caleth

ฉันไม่เข้าใจว่าคุณหมายถึงอะไรโดย [* wormholes, target] คุณจะอธิบายสิ่งนี้หรือไม่?
Jeff smith

1
"ระยะทางแบบยุคลิดกับระยะทางหนอนที่อยู่ใกล้ที่สุด" เป็นการประมาณการที่ถูกwormhole_path_distanceกว่าการค้นหากราฟย่อยและการดูถูกดูแคลนน้อยกว่า "ทางออกทั้งหมดอยู่ในเป้าหมาย"
Caleth

3
@ Caleth แน่นอน! มีความเป็นไปได้ในการปรับแต่งมากมายเช่นเราสามารถตัดสินใจล่วงหน้า n = 3 กระโดด การค้นหากราฟย่อยสอดคล้องกับการปิดของการกระโดดข้ามรูหนอนแบบวนทั้งหมด ข้อเสนอแนะของคุณที่จะมองไปข้างหน้า n = 1 การกระโดดเป็นสิ่งที่สวยงามมากเพราะมันมีค่าใช้จ่ายเพิ่มเติมเป็นศูนย์ :)
amon

1
สมมติว่ามีเพียงหนอนเดียว (สองโหนด) จากนั้นคุณสามารถแปลงระนาบนี้ 1- หนอนเป็นเครื่องบินกระจก 2 โดยคัดลอกเครื่องบินสมมาตรกับเส้นเท่ากันระหว่างสองจุดนี้เป็นแกนสมมาตร ตอนนี้คุณมีสองระนาบให้เรียกพวกมันว่าระนาบจริง (คุณไม่เอารูหนอน) และระนาบจินตภาพ (คุณเอารูหนอน) ตอนนี้เราแนะนำพิกัด Z พิกัดนี้จะเป็น 0 สำหรับทุกจุดในระนาบจริงและจะเป็น dist (หนอนหลุมชี้) สำหรับทุกจุดของระนาบจินตภาพ หลังจากนั้นใช้ A * สำหรับพื้นที่ 3 มิติ
lilezek

5

คุณมีกราฟที่มี 6 จุดยอดในตารางที่มีพิกัดดังนี้

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

คุณสามารถสร้างกราฟที่สมบูรณ์ในจุดยอดเหล่านั้นและกำหนดต้นทุนให้กับแต่ละขอบซึ่งค่าใช้จ่ายMAX( ABS( x1 - x2 ), ABS( y1 - y2 ) )สำหรับขอบมาตรฐานและค่า 0 สำหรับหนอนหลุม

สิ่งนี้จะทำให้คุณมีค่าใช้จ่าย (เป็นเมทริกซ์ adjacency):

   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  -

หากมีการแปรปรวนแบบทิศทางเดียวให้สร้างขอบในกราฟ (หรือเมทริกซ์ adjacency) ที่ไปในทิศทางนั้น แต่ไม่ใช่ในทางตรงกันข้าม

จากนั้นคุณสามารถใช้อัลกอริทึมของ Dijkstraกับคิวลำดับความสำคัญ

เริ่มต้นจากAและดันแต่ละขอบที่อยู่ติดกันบนคิวลำดับความสำคัญ:

รูปแบบ: (เส้นทาง: ค่าใช้จ่าย)

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

เมื่อรายการถูกส่งไปยังคิว - ติดตามค่าใช้จ่ายขั้นต่ำสำหรับแต่ละจุดสุดยอดและผลักเส้นทางไปยังคิวเท่านั้นหากมีราคาต่ำกว่าต้นทุนขั้นต่ำที่มีอยู่

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

ลบรายการแรกออกจากคิวและหากต้นทุนยังคงตรงกับต้นทุนขั้นต่ำให้ผลักเส้นทางคอมโพสิตทั้งหมดที่เกิดขึ้นโดยเส้นทางนั้นและขอบด้านข้างติดกันกลับสู่คิวลำดับความสำคัญ (หากเส้นทางคอมโพสิตมีต้นทุนต่ำกว่าขั้นต่ำที่มีอยู่):

ลบ: (A-B : 7)

  • ลอง(A-B-A : 14)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-B-C : 7)- ปฏิเสธค่าใช้จ่ายเดียวกัน
  • ลอง(A-B-D : 13)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-B-E : 19)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-B-F : 19)- ปฏิเสธตามต้นทุนที่สูงขึ้น

ลบ (A-C : 7)

  • ลอง(A-C-A : 14)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-C-B : 7)- ปฏิเสธค่าใช้จ่ายเดียวกัน
  • ลอง(A-C-D : 10)- ปฏิเสธค่าใช้จ่ายเดียวกัน
  • ลอง(A-C-E : 16)- ปฏิเสธค่าใช้จ่ายเดียวกัน
  • ลอง(A-C-F : 16)- ปฏิเสธค่าใช้จ่ายเดียวกัน

ลบ (A-D : 10)

  • ลอง(A-D-A : 20)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-D-B : 16)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-D-C : 13)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-D-E : 10)- แทรกลงในคิว
  • ลอง(A-D-F : 16)- ปฏิเสธค่าใช้จ่ายเดียวกัน

ตอนนี้คิวจะมีลักษณะดังนี้:

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 }

ลบ (A-D-E : 10)

  • ลอง(A-D-E-A : 26)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-D-E-B : 22)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-D-E-C : 19)- ปฏิเสธตามต้นทุนที่สูงขึ้น
  • ลอง(A-D-E-D : 10)- ปฏิเสธค่าใช้จ่ายเดียวกัน
  • ลอง(A-D-E-F : 12)- แทรกลงในคิว

จากนั้นคิวคือ:

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 }

ลบ(A-D-E-F : 12)พบว่าคุณได้ไปยังโหนดปลายทางในราคา 12

หมายเหตุ: เส้นทาง(A-B-C-D-E-F), (A-C-D-E-F)และ(A-D-E-F)ทุกคนมีค่าใช้จ่ายขั้นต่ำเดียวกันของ 12


0

ตั้งค่าเมทริกซ์ที่มีจุดยอดทั้งหมดและใช้ Floyd-Wallenstein-Algorithm หรือ Bellman-Ford-Algorithm ทั้งคู่จะส่งผลให้เกิดเมทริกซ์พร้อมเส้นทางที่สั้นที่สุดระหว่างจุดทั้งหมด จากนั้นคุณสามารถวนซ้ำผ่านเมทริกซ์เพื่อค้นหาเส้นทางที่สั้นที่สุดที่เชื่อมต่อสองจุด (ปัญหาของคุณเหมือนกับ TSP ไม่สมดุล)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.