ทำไมอัลกอริทึมของ Dijkstra ไม่ทำงานสำหรับขอบน้ำหนักเชิงลบ


121

ใครช่วยบอกหน่อยได้ไหมว่าทำไมอัลกอริทึมของ Dijkstra สำหรับพา ธ ที่สั้นที่สุดของแหล่งเดียวจึงถือว่าขอบต้องไม่เป็นลบ

ฉันกำลังพูดถึงเฉพาะขอบไม่ใช่รอบน้ำหนักเชิงลบ


3
คำตอบที่ถูกต้องพร้อมตัวอย่างที่ดีคือstackoverflow.com/questions/6799172/…
Amitk

คำตอบ:


175

โปรดจำไว้ว่าในอัลกอริทึมของ 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) ในการทำเช่นนั้น


5
ขออภัยฉันไม่ได้รับข้อผิดพลาดใด ๆ ครั้งแรกA->Bจะ 5 และA->Cจะ 2. จากนั้นก็จะB->C -5ดังนั้นมูลค่าCจะ-5เท่ากับ Bellman-Ford นี่ไม่ให้คำตอบที่ถูกต้องได้อย่างไร?
Anirban Nag 'tintinmj'

5
@tintinmj ก่อน Dijkstra จะ "ปิด" โหนดที่Aมีค่าเป็น 0 จากนั้นจะดูโหนดที่มีค่าน้อยที่สุดBคือ 5 และCเป็น 2 ค่าน้อยที่สุดคือCดังนั้นมันจะปิดCด้วยค่า 2 และจะไม่มองย้อนกลับไปเมื่อ ในภายหลังBถูกปิดไม่สามารถแก้ไขค่าของCได้เนื่องจาก "ปิด" อยู่แล้ว
Amit

4
@amit อัลกอริทึมของ Dijkstra จะไม่พบเส้นทางได้A -> B -> Cอย่างไร? ขั้นแรกจะอัปเดตCระยะทางเป็น 2 จากนั้นBระยะทางเป็น 5 โดยสมมติว่าในกราฟของคุณไม่มีขอบขาออกจากCนั้นเราจะไม่ทำอะไรเลยเมื่อเข้าชมC(และระยะทางยังคงเป็น 2) จากนั้นเราไปDที่โหนดที่อยู่ติดกันและโหนดที่อยู่ติดกันเพียงโหนดCเดียวซึ่งมีระยะทางใหม่คือ -5 โปรดทราบว่าในอัลกอริทึมของ Dijkstra เรายังติดตามพาเรนต์ที่เราไปถึง (และอัปเดต) โหนดและดำเนินการต่อจากCนั้นคุณจะได้รับพาเรนBต์และจากนั้นAผลลัพธ์ที่ถูกต้อง ฉันขาดอะไรไป?
nbro

12
@amit ปัญหาเกี่ยวกับการให้เหตุผลของคุณ (ฉันคิดว่า) และฉันเคยเห็นคนอื่นทำมัน (แปลก ๆ ) คือคุณคิดว่าอัลกอริทึมจะไม่พิจารณาโหนดที่มีการกำหนดระยะทางที่สั้นที่สุดแล้ว (และเราทำเสร็จแล้ว) แต่สิ่งนี้ไม่ถูกต้องและนั่นคือเหตุผลที่เรามีขั้นตอน "การผ่อนคลาย" ... เราวนซ้ำผ่านโหนดทั้งหมดของกราฟและสำหรับแต่ละโหนดเราจะวนซ้ำผ่านโหนดที่อยู่ติดกันแม้ว่าโหนดใด ๆ ที่อยู่ติดกันอาจ ได้ถูกลบออกจากคิวลำดับความสำคัญขั้นต่ำของเราแล้วเช่น
nbro

10
@amit ตรวจสอบคำตอบนี้สำหรับคำถามที่คล้ายกันโดยที่ตัวอย่างมีเหตุผลจริงๆ: stackoverflow.com/a/6799344/3924118
nbro

37

พิจารณากราฟที่แสดงด้านล่างโดยมีแหล่งที่มาเป็น Vertex A ก่อนอื่นให้ลองเรียกใช้อัลกอริทึมของ Dijkstra ด้วยตัวคุณเอง

ใส่คำอธิบายภาพที่นี่

เมื่อฉันอ้างถึงอัลกอริทึมของ Dijkstra ในคำอธิบายของฉันฉันจะพูดถึงอัลกอริทึมของ Dijkstra ตามที่ดำเนินการด้านล่าง

อัลกอริทึมของ Dijkstra

ดังนั้นการเริ่มต้นค่า ( ระยะห่างจากแหล่งที่มาถึงจุดยอด ) ในขั้นต้นที่กำหนดให้กับจุดยอดแต่ละจุดคือ

การเริ่มต้น

ครั้งแรกที่เราสกัดจุดสุดยอดในQ = [A, B, C]ซึ่งมีค่าที่น้อยที่สุดคือหลังจากที่Q = [B, C] หมายเหตุ A มีขอบกำกับไปที่ B และ C ทั้งคู่อยู่ใน Q ดังนั้นเราจึงอัปเดตทั้งสองค่า

การทำซ้ำครั้งแรก

ตอนนี้เราแยกซี (2 <5) ตอนนี้Q = [B] โปรดทราบว่า C ไม่ได้เชื่อมต่อกับอะไรเลยline16ลูปจึงไม่ทำงาน

การทำซ้ำครั้งที่สอง

ในที่สุดเราก็แยก B หลังจากQ คือพี่นั้น หมายเหตุ B มีขอบกำกับไปที่ C แต่ C นั้นไม่ได้อยู่ในคิวอีกครั้งดังนั้นเราจึงไม่ได้เข้าสู่ห่วงในline16,

3?

ดังนั้นเราจึงจบลงด้วยระยะทางเป็น

ไม่มีคนเปลี่ยนแปลง

สังเกตว่าสิ่งนี้ผิดอย่างไรเนื่องจากระยะทางที่สั้นที่สุดจาก A ถึง C คือ 5 + -10 = -5 เมื่อคุณไป a ถึง b ถึง cเมื่อคุณไป

ดังนั้นสำหรับอัลกอริทึมของ Dijkstra กราฟนี้คำนวณระยะทางจาก A ถึง C อย่างผิดพลาด

สิ่งนี้เกิดขึ้นเนื่องจากอัลกอริทึมของ Dijkstra ไม่พยายามค้นหาเส้นทางที่สั้นกว่าไปยังจุดยอดซึ่งดึงมาจาก Q แล้วแล้ว

สิ่งที่line16ห่วงจะทำคือการจุดสุดยอดยูและพูดว่า"เฮ้ดูเหมือนว่าเราสามารถไปที่โวลต์จากแหล่งที่ผ่านยูเป็นที่ (Alt หรือทางเลือก) ระยะทางใด ๆ ที่ดีกว่าปัจจุบันอ [V]เราได้หรือไม่ถ้าใช่ช่วยปรับปรุงระยะ [v] "

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

ในตัวอย่างของเราด้านบน C เป็นเพื่อนบ้านที่มาเยี่ยมของ B ดังนั้นจึงA ถึง B ถึง Cไม่พิจารณาเส้นทางโดยปล่อยให้เส้นทางที่สั้นที่สุดในปัจจุบันA ถึง Cไม่เปลี่ยนแปลง

สิ่งนี้มีประโยชน์จริง ๆหากน้ำหนักขอบเป็นจำนวนบวกทั้งหมดเพราะเราจะไม่เสียเวลาพิจารณาเส้นทางที่ไม่สามารถสั้นกว่านี้ได้

ดังนั้นฉันจึงบอกว่าเมื่อเรียกใช้อัลกอริทึมนี้ถ้า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


2
อย่างที่คุณพูดวิธีที่ดีกว่าในการแก้ปัญหานี้คือใช้ heapq.heappush หลังจากการเปรียบเทียบแต่ละครั้ง เราดันระยะทางที่อัปเดตกลับเข้าไปในคิว ภายใต้เงื่อนไขนี้ Dijkstra สามารถทำงานกับน้ำหนักที่เป็นลบได้ ฉันลองแล้วผลลัพธ์ออกมาเป็น 0,5, -5
ไร้สาระ

1
"แหล่งที่มาเส้นทางไปยัง x ถึง u ไม่ได้รับการพิจารณาด้วยซ้ำ"; คุณหมายถึงแหล่งที่มาของ u ถึง x หรือเปล่า
slmatrix

1
@slmatrix ขอบคุณที่จับได้ว่าใช่ฉันหมายความว่าเส้นทางจากต้นทางถึง u ถึง x เพราะ x เป็นเพื่อนบ้านของคุณ
Aditya P

23

อัลกอริทึมของ Dijkstra ถือว่าเส้นทางสามารถ 'หนักขึ้น' เท่านั้นดังนั้นถ้าคุณมีเส้นทางจาก A ถึง B ด้วยน้ำหนัก 3 และเส้นทางจาก A ถึง C ด้วยน้ำหนัก 3 ไม่มีทางที่คุณจะเพิ่มขอบและ ได้รับจาก A ถึง B ถึง C โดยมีน้ำหนักน้อยกว่า 3

สมมติฐานนี้ทำให้อัลกอริทึมเร็วกว่าอัลกอริทึมที่ต้องคำนึงถึงน้ำหนักเชิงลบ


8

ความถูกต้องของอัลกอริทึมของ Dijkstra:

เรามีจุดยอด 2 ชุดในทุกขั้นตอนของอัลกอริทึม ชุด A ประกอบด้วยจุดยอดที่เราคำนวณเส้นทางที่สั้นที่สุด ชุด B ประกอบด้วยจุดยอดที่เหลือ

สมมติฐานอุปนัย : ในแต่ละขั้นตอนเราจะถือว่าการทำซ้ำก่อนหน้านี้ทั้งหมดถูกต้อง

ขั้นตอนอุปนัย : เมื่อเราเพิ่มจุดยอด V ให้กับเซต A และตั้งค่าระยะทางเป็น dist [V] เราต้องพิสูจน์ว่าระยะนี้เหมาะสมที่สุด หากไม่เหมาะสมก็ต้องมีเส้นทางอื่นไปยังจุดยอด V ที่มีความยาวสั้นกว่า

สมมติว่านี่คือเส้นทางอื่นผ่านจุดยอด X

ตอนนี้เนื่องจาก dist [V] <= dist [X] ดังนั้นเส้นทางอื่นที่ไปยัง V จะมีความยาวอย่างน้อย dist [V] เว้นแต่ว่ากราฟจะมีความยาวขอบเป็นลบ

ดังนั้นเพื่อให้อัลกอริทึมของ dijkstra ทำงานได้น้ำหนักขอบจะต้องไม่เป็นลบ


6

ลองใช้อัลกอริทึมของ Dijkstra ในกราฟต่อไปนี้โดยสมมติว่าAเป็นโหนดต้นทางเพื่อดูว่าเกิดอะไรขึ้น:

กราฟ


6
ขออภัยฉันไม่ได้รับข้อผิดพลาดใด ๆ ครั้งแรกA->Bจะ1และจะA->C 100แล้วB->Dจะ2. แล้วC->Dจะ-4900. ดังนั้นมูลค่าDจะ-4900เท่ากับ Bellman-Ford นี่ไม่ให้คำตอบที่ถูกต้องได้อย่างไร?
Anirban Nag 'tintinmj'

9
@tintinmj หากคุณมีขอบขาออกจาก D มันจะถูกเยี่ยมก่อนที่ระยะห่างของ D จะลดลงและด้วยเหตุนี้จึงไม่ได้รับการปรับปรุงหลังจากนั้น จากนั้นจะส่งผลให้เกิดข้อผิดพลาดอย่างแน่นอน หากคุณถือว่า D's 2 เป็นระยะทางสุดท้ายหลังจากสแกนขอบขาออกแล้วแม้กราฟนี้จะเกิดข้อผิดพลาด
Christian Schnorr

@ tb- ขอโทษที่ถามหลังจากนั้นนานมาก แต่ฉันมาถูกทางแล้วใช่ไหม ครั้งแรกA->Bจะเป็น1และจะเป็นA->C 100จากนั้นBคือการสำรวจและชุดที่จะB->D 2แล้วDถูกสำรวจเพราะปัจจุบันมีเส้นทางกลับไปยังต้นทางที่สั้นที่สุด? ผมจะมีความถูกต้องในการบอกว่าถ้าB->Dเป็น100, Cจะได้รับการสำรวจครั้งแรก? ฉันเข้าใจตัวอย่างอื่น ๆ ทั้งหมดยกเว้นของคุณ
Pejman Poh

@PejmanPoh จากความเข้าใจของฉันถ้า B-> D คือ 100 เนื่องจาก A-> C สูงกว่าใน HeapStructure ซึ่งจะใช้ min extract จะส่งกลับ A-> C ก่อนซึ่งหมายความว่าเส้นทางที่สั้นที่สุดที่พบถัดไปจะเป็นเส้นทาง ถึง C หลังจากนั้นเส้นทางจาก C-> D ซึ่งมีน้ำหนัก -5000 จะเป็นตัวเลือกที่ชัดเจนทำให้เราได้ข้อสรุปว่าเส้นทางที่สั้นที่สุดน่าจะมาจาก A-> C-> D และฉันค่อนข้างมั่นใจว่านี่จะเป็น เป็นพฤติกรรมปกติ ดังนั้นบางครั้งเมื่อเรามีวัฏจักรเชิงลบเราก็ยังอาจได้ค่าที่เหมาะสมสำหรับเส้นทางที่สั้นที่สุด แต่ก็ไม่เสมอไปนี่คือตัวอย่างที่เราจะไม่ ..
T.Dimitrov

1

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


0

คำตอบอื่น ๆ แสดงให้เห็นได้ดีว่าทำไมอัลกอริทึมของ Dijkstra จึงไม่สามารถจัดการน้ำหนักเชิงลบบนเส้นทางได้

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

พิจารณาสิ่งนี้:

A  <- 5 ->  B  <- (-1) ->  C <- 5 -> D

เส้นทางที่ดีที่สุดระหว่าง A และ D คืออะไร?

อัลกอริธึมการค้นหาเส้นทางใด ๆ จะต้องวนซ้ำอย่างต่อเนื่องระหว่าง B และ C เนื่องจากการทำเช่นนั้นจะลดน้ำหนักของเส้นทางทั้งหมด ดังนั้นการอนุญาตให้ใช้น้ำหนักเชิงลบสำหรับการเชื่อมต่อจะทำให้อัลกอริทึม pathfindig moot ใด ๆ อาจจะยกเว้นว่าคุณ จำกัด การเชื่อมต่อแต่ละรายการให้ใช้เพียงครั้งเดียว


0

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

ในกรณีนี้ฉันเห็นว่าควรใช้อัลกอริธึม SPFAซึ่งมีคิวปกติและสามารถจัดการกับขอบลบได้ดีกว่า

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