ใครช่วยบอกหน่อยได้ไหมว่าทำไมอัลกอริทึมของ Dijkstra สำหรับพา ธ ที่สั้นที่สุดของแหล่งเดียวจึงถือว่าขอบต้องไม่เป็นลบ
ฉันกำลังพูดถึงเฉพาะขอบไม่ใช่รอบน้ำหนักเชิงลบ
ใครช่วยบอกหน่อยได้ไหมว่าทำไมอัลกอริทึมของ Dijkstra สำหรับพา ธ ที่สั้นที่สุดของแหล่งเดียวจึงถือว่าขอบต้องไม่เป็นลบ
ฉันกำลังพูดถึงเฉพาะขอบไม่ใช่รอบน้ำหนักเชิงลบ
คำตอบ:
โปรดจำไว้ว่าในอัลกอริทึมของ Dijkstra เมื่อจุดยอดถูกทำเครื่องหมายเป็น "ปิด" (และอยู่นอกชุดเปิด) - อัลกอริทึมพบเส้นทางที่สั้นที่สุดและจะไม่ต้องพัฒนาโหนดนี้อีก - ถือว่าเส้นทางที่พัฒนาขึ้นสำหรับสิ่งนี้ เส้นทางที่สั้นที่สุด
แต่ด้วยน้ำหนักเชิงลบ - อาจไม่เป็นความจริง ตัวอย่างเช่น:
A
/ \
/ \
/ \
5 2
/ \
B--(-10)-->C
V={A,B,C} ; E = {(A,C,2), (A,B,5), (B,C,-10)}
Dijkstra จาก A จะพัฒนา C ก่อนและจะหาไม่พบในภายหลัง A->B->C
แก้ไขคำอธิบายที่ลึกขึ้นเล็กน้อย:
โปรดทราบว่าสิ่งนี้มีความสำคัญเนื่องจากในแต่ละขั้นตอนการผ่อนคลายอัลกอริทึมจะถือว่า "ต้นทุน" ของโหนด "ปิด" นั้นน้อยมากดังนั้นโหนดที่จะเลือกถัดไปจึงมีน้อยด้วย
แนวคิดของมันคือ: ถ้าเรามีจุดยอดอยู่ในที่เปิดซึ่งต้นทุนของมันจะน้อยที่สุด - โดยการเพิ่มจำนวนบวกใด ๆ ลงในจุดยอดใด ๆค่าขั้นต่ำจะไม่มีวันเปลี่ยนแปลง
หากไม่มีข้อ จำกัด เกี่ยวกับจำนวนบวก - สมมติฐานข้างต้นไม่เป็นความจริง
เนื่องจากเรา "รู้" แต่ละจุดยอดซึ่ง "ปิด" จึงมีน้อยมากเราจึงสามารถทำขั้นตอนการผ่อนคลายได้อย่างปลอดภัยโดยไม่ต้อง "มองย้อนกลับ" หากเราจำเป็นต้อง "มองย้อนกลับไป" - Bellman-Fordนำเสนอโซลูชันแบบวนซ้ำ (DP) ในการทำเช่นนั้น
A->B
จะ 5 และA->C
จะ 2. จากนั้นก็จะB->C
-5
ดังนั้นมูลค่าC
จะ-5
เท่ากับ Bellman-Ford นี่ไม่ให้คำตอบที่ถูกต้องได้อย่างไร?
A
มีค่าเป็น 0 จากนั้นจะดูโหนดที่มีค่าน้อยที่สุดB
คือ 5 และC
เป็น 2 ค่าน้อยที่สุดคือC
ดังนั้นมันจะปิดC
ด้วยค่า 2 และจะไม่มองย้อนกลับไปเมื่อ ในภายหลังB
ถูกปิดไม่สามารถแก้ไขค่าของC
ได้เนื่องจาก "ปิด" อยู่แล้ว
A -> B -> C
อย่างไร? ขั้นแรกจะอัปเดตC
ระยะทางเป็น 2 จากนั้นB
ระยะทางเป็น 5 โดยสมมติว่าในกราฟของคุณไม่มีขอบขาออกจากC
นั้นเราจะไม่ทำอะไรเลยเมื่อเข้าชมC
(และระยะทางยังคงเป็น 2) จากนั้นเราไปD
ที่โหนดที่อยู่ติดกันและโหนดที่อยู่ติดกันเพียงโหนดC
เดียวซึ่งมีระยะทางใหม่คือ -5 โปรดทราบว่าในอัลกอริทึมของ Dijkstra เรายังติดตามพาเรนต์ที่เราไปถึง (และอัปเดต) โหนดและดำเนินการต่อจากC
นั้นคุณจะได้รับพาเรนB
ต์และจากนั้นA
ผลลัพธ์ที่ถูกต้อง ฉันขาดอะไรไป?
เมื่อฉันอ้างถึงอัลกอริทึมของ Dijkstra ในคำอธิบายของฉันฉันจะพูดถึงอัลกอริทึมของ Dijkstra ตามที่ดำเนินการด้านล่าง
ดังนั้นการเริ่มต้นค่า ( ระยะห่างจากแหล่งที่มาถึงจุดยอด ) ในขั้นต้นที่กำหนดให้กับจุดยอดแต่ละจุดคือ
ครั้งแรกที่เราสกัดจุดสุดยอดในQ = [A, B, C]ซึ่งมีค่าที่น้อยที่สุดคือหลังจากที่Q = [B, C] หมายเหตุ A มีขอบกำกับไปที่ B และ C ทั้งคู่อยู่ใน Q ดังนั้นเราจึงอัปเดตทั้งสองค่า
ตอนนี้เราแยกซี (2 <5) ตอนนี้Q = [B] โปรดทราบว่า C ไม่ได้เชื่อมต่อกับอะไรเลยline16
ลูปจึงไม่ทำงาน
ในที่สุดเราก็แยก B หลังจากนั้น หมายเหตุ B มีขอบกำกับไปที่ C แต่ C นั้นไม่ได้อยู่ในคิวอีกครั้งดังนั้นเราจึงไม่ได้เข้าสู่ห่วงในline16
,
ดังนั้นเราจึงจบลงด้วยระยะทางเป็น
สังเกตว่าสิ่งนี้ผิดอย่างไรเนื่องจากระยะทางที่สั้นที่สุดจาก A ถึง C คือ 5 + -10 = -5 เมื่อคุณไป เมื่อคุณไป
ดังนั้นสำหรับอัลกอริทึมของ Dijkstra กราฟนี้คำนวณระยะทางจาก A ถึง C อย่างผิดพลาด
สิ่งนี้เกิดขึ้นเนื่องจากอัลกอริทึมของ Dijkstra ไม่พยายามค้นหาเส้นทางที่สั้นกว่าไปยังจุดยอดซึ่งดึงมาจาก Q แล้วแล้ว
สิ่งที่line16
ห่วงจะทำคือการจุดสุดยอดยูและพูดว่า"เฮ้ดูเหมือนว่าเราสามารถไปที่โวลต์จากแหล่งที่ผ่านยูเป็นที่ (Alt หรือทางเลือก) ระยะทางใด ๆ ที่ดีกว่าปัจจุบันอ [V]เราได้หรือไม่ถ้าใช่ช่วยปรับปรุงระยะ [v] "
ทราบว่าในline16
พวกเขาตรวจสอบทุกเพื่อนบ้านวี (เช่นขอบกำกับอยู่จากu เพื่อโวลต์ ) ของยูซึ่งยังคงอยู่ในคิว ในline14
พวกเขาเอาบันทึกการเข้าเยี่ยมชมจาก Q. ดังนั้นถ้าxเป็นเพื่อนบ้านเข้าเยี่ยมชมของยูเส้นทางจะไม่ได้พิจารณาถึงเป็นวิธีที่สั้นที่สุดจากแหล่งที่โวลต์
ในตัวอย่างของเราด้านบน C เป็นเพื่อนบ้านที่มาเยี่ยมของ B ดังนั้นจึงไม่พิจารณาเส้นทางโดยปล่อยให้เส้นทางที่สั้นที่สุดในปัจจุบันไม่เปลี่ยนแปลง
สิ่งนี้มีประโยชน์จริง ๆหากน้ำหนักขอบเป็นจำนวนบวกทั้งหมดเพราะเราจะไม่เสียเวลาพิจารณาเส้นทางที่ไม่สามารถสั้นกว่านี้ได้
ดังนั้นฉันจึงบอกว่าเมื่อเรียกใช้อัลกอริทึมนี้ถ้าxถูกแยกออกจาก Q ก่อนyจะไม่สามารถหาเส้นทางได้ซึ่งสั้นกว่า ให้ฉันอธิบายสิ่งนี้ด้วยตัวอย่าง
ในฐานะที่เป็นปีเพิ่งได้รับการสกัดและxได้รับการสกัดก่อนที่ตัวเองแล้วDIST [Y]> อ [x]เพราะมิฉะนั้นปีจะได้รับการสกัดก่อนx ( line 13
ระยะทางขั้นต่ำก่อน)
และในขณะที่เรามีอยู่แล้วสันนิษฐานว่าน้ำหนักขอบเป็นบวกคือความยาว (x, y)> 0 ดังนั้นระยะทางเลือก (Alt) ผ่านYอยู่เสมอให้แน่ใจว่าจะมากขึ้นเช่นอ [Y] + ยาว (x, y)> อ [x] ดังนั้นค่าของdist [x]จะไม่ได้รับการอัปเดตแม้ว่าyจะถูกพิจารณาว่าเป็นพา ธ ไปยังxดังนั้นเราจึงสรุปได้ว่าควรพิจารณาเฉพาะเพื่อนบ้านของyซึ่งยังอยู่ใน Q (หมายเหตุความคิดเห็นในline16
)
แต่สิ่งนี้บานพับบนสมมติฐานของความยาวขอบในเชิงบวกของเราถ้ามีความยาว (U, V) <0แล้วขึ้นอยู่กับวิธีเชิงลบที่ขอบคือเราอาจจะมาแทนที่อ [x]line18
หลังจากการเปรียบเทียบใน
ดังนั้นการคำนวณdist [x]ใด ๆ ที่เราทำจะไม่ถูกต้องถ้าxถูกลบออกก่อนจุดยอดทั้งหมดv - ดังนั้นxคือเพื่อนบ้านของv ที่มีขอบลบเชื่อมต่อ - จะถูกลบออก
เนื่องจากจุดยอดvแต่ละจุดเป็นจุดยอดสุดท้ายที่สองบนเส้นทางที่ "ดีกว่า" ที่เป็นไปได้จากแหล่งที่มาถึงxซึ่งถูกละทิ้งโดยอัลกอริทึมของ Dijkstra
ดังนั้นในตัวอย่างที่ฉันให้ไว้ข้างต้นความผิดพลาดคือเพราะ C ถูกลบออกก่อนที่ B จะถูกลบออก ในขณะที่ C นั้นเป็นเพื่อนบ้านของ B ที่มีค่าลบ!
แค่ชี้แจงว่า B และ C เป็นเพื่อนบ้านของ A B มีเพื่อนบ้านคนเดียว C และ C ไม่มีเพื่อนบ้าน ความยาว (a, b) คือความยาวขอบระหว่างจุดยอด a และ b
อัลกอริทึมของ Dijkstra ถือว่าเส้นทางสามารถ 'หนักขึ้น' เท่านั้นดังนั้นถ้าคุณมีเส้นทางจาก A ถึง B ด้วยน้ำหนัก 3 และเส้นทางจาก A ถึง C ด้วยน้ำหนัก 3 ไม่มีทางที่คุณจะเพิ่มขอบและ ได้รับจาก A ถึง B ถึง C โดยมีน้ำหนักน้อยกว่า 3
สมมติฐานนี้ทำให้อัลกอริทึมเร็วกว่าอัลกอริทึมที่ต้องคำนึงถึงน้ำหนักเชิงลบ
ความถูกต้องของอัลกอริทึมของ Dijkstra:
เรามีจุดยอด 2 ชุดในทุกขั้นตอนของอัลกอริทึม ชุด A ประกอบด้วยจุดยอดที่เราคำนวณเส้นทางที่สั้นที่สุด ชุด B ประกอบด้วยจุดยอดที่เหลือ
สมมติฐานอุปนัย : ในแต่ละขั้นตอนเราจะถือว่าการทำซ้ำก่อนหน้านี้ทั้งหมดถูกต้อง
ขั้นตอนอุปนัย : เมื่อเราเพิ่มจุดยอด V ให้กับเซต A และตั้งค่าระยะทางเป็น dist [V] เราต้องพิสูจน์ว่าระยะนี้เหมาะสมที่สุด หากไม่เหมาะสมก็ต้องมีเส้นทางอื่นไปยังจุดยอด V ที่มีความยาวสั้นกว่า
สมมติว่านี่คือเส้นทางอื่นผ่านจุดยอด X
ตอนนี้เนื่องจาก dist [V] <= dist [X] ดังนั้นเส้นทางอื่นที่ไปยัง V จะมีความยาวอย่างน้อย dist [V] เว้นแต่ว่ากราฟจะมีความยาวขอบเป็นลบ
ดังนั้นเพื่อให้อัลกอริทึมของ dijkstra ทำงานได้น้ำหนักขอบจะต้องไม่เป็นลบ
ลองใช้อัลกอริทึมของ Dijkstra ในกราฟต่อไปนี้โดยสมมติว่าA
เป็นโหนดต้นทางเพื่อดูว่าเกิดอะไรขึ้น:
A->B
จะ1
และจะA->C
100
แล้วB->D
จะ2
. แล้วC->D
จะ-4900
. ดังนั้นมูลค่าD
จะ-4900
เท่ากับ Bellman-Ford นี่ไม่ให้คำตอบที่ถูกต้องได้อย่างไร?
A->B
จะเป็น1
และจะเป็นA->C
100
จากนั้นB
คือการสำรวจและชุดที่จะB->D
2
แล้วDถูกสำรวจเพราะปัจจุบันมีเส้นทางกลับไปยังต้นทางที่สั้นที่สุด? ผมจะมีความถูกต้องในการบอกว่าถ้าB->D
เป็น100
, C
จะได้รับการสำรวจครั้งแรก? ฉันเข้าใจตัวอย่างอื่น ๆ ทั้งหมดยกเว้นของคุณ
โปรดจำไว้ว่าในอัลกอริทึมของ Dijkstra เมื่อจุดยอดถูกทำเครื่องหมายเป็น "ปิด" (และอยู่นอกชุดเปิด) - ถือว่าโหนดใด ๆ ที่มาจากจุดนั้นจะนำไปสู่ระยะทางที่มากขึ้นดังนั้นอัลกอริทึมจึงพบเส้นทางที่สั้นที่สุดและจะ ไม่ต้องพัฒนาโหนดนี้อีก แต่จะไม่เป็นจริงในกรณีที่มีน้ำหนักติดลบ
คำตอบอื่น ๆ แสดงให้เห็นได้ดีว่าทำไมอัลกอริทึมของ Dijkstra จึงไม่สามารถจัดการน้ำหนักเชิงลบบนเส้นทางได้
แต่คำถามนั้นอาจมาจากความเข้าใจที่ไม่ถูกต้องเกี่ยวกับน้ำหนักของเส้นทาง หากอนุญาตให้ใช้น้ำหนักเชิงลบบนเส้นทางในอัลกอริทึมการค้นหาพา ธ โดยทั่วไปคุณจะได้รับลูปถาวรที่ไม่หยุดนิ่ง
พิจารณาสิ่งนี้:
A <- 5 -> B <- (-1) -> C <- 5 -> D
เส้นทางที่ดีที่สุดระหว่าง A และ D คืออะไร?
อัลกอริธึมการค้นหาเส้นทางใด ๆ จะต้องวนซ้ำอย่างต่อเนื่องระหว่าง B และ C เนื่องจากการทำเช่นนั้นจะลดน้ำหนักของเส้นทางทั้งหมด ดังนั้นการอนุญาตให้ใช้น้ำหนักเชิงลบสำหรับการเชื่อมต่อจะทำให้อัลกอริทึม pathfindig moot ใด ๆ อาจจะยกเว้นว่าคุณ จำกัด การเชื่อมต่อแต่ละรายการให้ใช้เพียงครั้งเดียว
คุณสามารถใช้อัลกอริทึมของ dijkstra ที่มีขอบลบไม่รวมถึงวัฏจักรเชิงลบ แต่คุณต้องอนุญาตให้สามารถเยี่ยมชมจุดยอดได้หลายครั้งและเวอร์ชันนั้นจะสูญเสียความซับซ้อนของเวลาที่รวดเร็ว
ในกรณีนี้ฉันเห็นว่าควรใช้อัลกอริธึม SPFAซึ่งมีคิวปกติและสามารถจัดการกับขอบลบได้ดีกว่า