Bobot negatif menggunakan Algoritma Dijkstra


113

Saya mencoba memahami mengapa algoritma Dijkstra tidak akan bekerja dengan bobot negatif. Membaca contoh di Jalur Terpendek , saya mencoba mencari tahu skenario berikut:

    2
A-------B
 \     /
3 \   / -2
   \ /
    C

Dari situs web:

Dengan asumsi edge semua terarah dari kiri ke kanan, Jika kita mulai dengan A, algoritma Dijkstra akan memilih edge (A, x) meminimalkan d (A, A) + length (edge), yaitu (A, B). Ini kemudian menetapkan d (A, B) = 2 dan memilih sisi lain (y, C) meminimalkan d (A, y) + d (y, C); satu-satunya pilihan adalah (A, C) dan menetapkan d (A, C) = 3. Tetapi ia tidak pernah menemukan jalur terpendek dari A ke B, melalui C, dengan total panjang 1.

Saya tidak dapat memahami mengapa menggunakan implementasi Dijkstra berikut, d [B] tidak akan diperbarui ke 1(Ketika algoritme mencapai simpul C, itu akan berjalan santai di B, lihat bahwa d [B] sama dengan 2, dan karenanya perbarui nilainya menjadi 1).

Dijkstra(G, w, s)  {
   Initialize-Single-Source(G, s)
   S ← Ø
   Q ← V[G]//priority queue by d[v]
   while Q ≠ Ø do
      u ← Extract-Min(Q)
      S ← S U {u}
      for each vertex v in Adj[u] do
         Relax(u, v)
}

Initialize-Single-Source(G, s) {
   for each vertex v  V(G)
      d[v] ← ∞
      π[v] ← NIL
   d[s] ← 0
}

Relax(u, v) {
   //update only if we found a strictly shortest path
   if d[v] > d[u] + w(u,v) 
      d[v] ← d[u] + w(u,v)
      π[v] ← u
      Update(Q, v)
}

Terima kasih,

Meir


Pencarian jalan secara umum dengan bobot tepi negatif sangat sulit. Tidak peduli rute apa yang Anda temukan, selalu ada kemungkinan rute yang sangat panjang dengan bobot tepi negatif yang sangat besar di suatu tempat di sepanjang itu. Saya tidak akan terkejut jika NP selesai.
Nick Johnson

4
Bagi orang lain yang memiliki keraguan ini, Anda dapat menemukan jalur terpendek dalam grafik DIBERIKAN yang tidak memiliki siklus berbobot negatif. Algoritme di atas akan bekerja jika fungsi Relax mengembalikan nilai "true" ketika relax benar-benar berhasil, dalam hal ini, simpul "v" yang berdekatan akan diantrekan dalam antrian prioritas jika tidak ada, atau diperbarui jika sudah ada. Ini berarti node yang dikunjungi dapat ditambahkan lagi ke antrean prioritas saat node tersebut semakin santai.
goelakash

Jawaban:


202

Algoritme yang Anda sarankan memang akan menemukan jalur terpendek dalam grafik ini, tetapi tidak semua grafik secara umum. Misalnya, perhatikan grafik ini:

Gambar grafik

Asumsikan tepi diarahkan dari kiri ke kanan seperti pada contoh Anda,

Algoritme Anda akan berfungsi sebagai berikut:

  1. Pertama, Anda menyetel d(A)ke zerodan jarak lainnya ke infinity.
  2. Anda kemudian memperluas node A, menyetel d(B)ke 1, d(C)ke zero, dan d(D)ke 99.
  3. Selanjutnya, Anda melakukan ekspansi C, tanpa perubahan bersih.
  4. Anda kemudian mengembangkannya B, yang tidak berpengaruh.
  5. Akhirnya, Anda memperluas D, yang berubah d(B)menjadi -201.

Perhatikan bahwa pada akhir ini, meskipun, itu d(C)tetap 0, meskipun jalur terpendek Cmemiliki panjang -200. Algoritme Anda karenanya gagal menghitung jarak secara akurat dalam beberapa kasus. Selain itu, bahkan jika Anda menyimpan kembali petunjuk yang mengatakan bagaimana untuk pergi dari setiap node ke node awal A, Anda akan berakhir mengambil jalur yang salah kembali dari Cke A.


35
Untuk menambah jawaban Anda yang luar biasa: Dijkstra menjadi algoritma yang rakus adalah alasan untuk pilihannya yang picik.
blubb

4
Saya ingin menunjukkan bahwa, secara teknis, semua jalur dalam grafik ini memiliki biaya tak terhingga negatif berkat siklus negatif A, D, B, A.
Nate

2
@ Nate- Untuk memperjelas, semua tepi pada grafik diarahkan dari kiri ke kanan. Agak sulit untuk membuat panah dalam seni ASCII saya yang berkualitas tinggi. :-)
templatetypedef

2
Bagi mereka yang belum pernah melihat grafik dengan tepi negatif sebelumnya, saya menemukan interpretasi yang berguna dari grafik ini sebagai jaringan jalan tol, di mana bobot tepi memberikan tol yang Anda bayarkan. Jalan -300 adalah jalan tol mundur yang gila di mana mereka memberi Anda $ 300 sebagai gantinya.
D Coetzee

3
@ SchwitJanwityanujit- Ini adalah cara kerja algoritma Dijkstra. Algoritme tidak menjelajahi jalur , tetapi bekerja dengan memproses node . Setiap node diproses tepat satu kali, jadi segera setelah kami memproses node B dan mendapatkan jaraknya 1, kami tidak akan mengunjungi kembali node B atau mencoba memperbarui jaraknya.
templatetypedef

25

Perhatikan, Dijkstra bekerja bahkan untuk bobot negatif, jika Grafik tidak memiliki siklus negatif, yaitu siklus yang bobotnya dijumlahkan kurang dari nol.

Mungkin ada yang bertanya, kenapa pada contoh yang dibuat oleh templatetypedef Dijkstra gagal padahal tidak ada siklus negatif, bahkan tidak ada siklus. Itu karena dia menggunakan kriteria stop lain, yang menahan algoritme segera setelah node target tercapai (atau semua node telah diselesaikan satu kali, dia tidak menentukannya dengan tepat). Dalam grafik tanpa bobot negatif, ini berfungsi dengan baik.

Jika seseorang menggunakan kriteria stop alternatif, yang menghentikan algoritma ketika prioritas-antrian (heap) berjalan kosong (kriteria stop ini juga digunakan dalam pertanyaan), maka dijkstra akan menemukan jarak yang benar bahkan untuk grafik dengan bobot negatif tapi tanpa siklus negatif.

Namun, dalam kasus ini, batas waktu asimtotik dijkstra untuk grafik tanpa siklus negatif hilang. Ini karena node yang sebelumnya diselesaikan dapat dimasukkan kembali ke heap ketika jarak yang lebih baik ditemukan karena bobot negatif. Properti ini disebut koreksi label.


2. Tidak jelas mengapa menurut Anda saat itu saya akan "lebih seperti Bellman-Ford" dan tidak eksponensial (yang lebih buruk dari Bellman-Ford). Apakah Anda memiliki algoritma konkret dan bukti dalam pikiran?
Gassa

3
To 1 .: karena Anda dapat menggunakan implementasi yang persis sama dari dijkstra dengan kriteria stop yang disebutkan, yang berhenti ketika antrian kosong (lihat pseudocode dalam pertanyaan asli), itu masih algoritma dijkstras untuk jalur terpendek, meskipun perilakunya berbeda menyelesaikan node beberapa kali (koreksi label).
infty10000101

1
Ke 2 .: Itu hanya tebakan jadi saya akan menghapusnya. Saya pikir Anda benar dengan waktu eksponensial, karena ada banyak jalur secara eksponensial, yang harus dieksplorasi.
infty10000101

11

Anda tidak menggunakan S di mana pun dalam algoritme Anda (selain memodifikasinya). Ide dari dijkstra adalah sekali sebuah simpul berada di S, itu tidak akan pernah dimodifikasi lagi. dalam hal ini, setelah B berada di dalam S, Anda tidak akan mencapainya lagi melalui C.

fakta ini memastikan kompleksitas O (E + VlogV) [jika tidak, Anda akan mengulang tepian lebih dari sekali, dan simpul lebih dari sekali]

dengan kata lain, algoritma yang Anda posting, mungkin tidak dalam O (E + VlogV), seperti yang dijanjikan oleh algoritma dijkstra.


Selain itu, tidak perlu memodifikasi titik tanpa tepi berbobot negatif, yang sepenuhnya mematahkan asumsi bahwa biaya jalur hanya dapat meningkat dengan tepi berulang
prusswan

Asumsi ini secara tepat memungkinkan kita untuk menggunakan S, dan 'mengetahui' begitu sebuah simpul ada di S, itu tidak akan pernah dimodifikasi lagi.
amit

Pernyataan terakhir Anda salah. Algoritme yang diposting memiliki kompleksitas waktu O (E + VlogV) saat bekerja pada grafik tanpa tepi negatif. Tidak perlu memeriksa bahwa kita telah mengunjungi sebuah node, karena fakta bahwa node telah dikunjungi menjamin prosedur relaksasi tidak akan menambahkannya sekali lagi dalam antrian.
Pixar

7

Karena Dijkstra adalah pendekatan Greedy, sekali simpul ditandai sebagai dikunjungi untuk loop ini, itu tidak akan pernah dievaluasi ulang lagi bahkan jika ada jalur lain dengan biaya lebih sedikit untuk mencapainya nanti. Dan masalah seperti itu hanya bisa terjadi jika tepi negatif ada di grafik.


Sebuah algoritma serakah , seperti namanya, selalu membuat pilihan yang tampaknya menjadi yang terbaik pada saat itu. Asumsikan bahwa Anda memiliki fungsi objektif yang perlu dioptimalkan (baik dimaksimalkan atau diminimalkan) pada titik tertentu. Algoritme Greedy membuat pilihan serakah di setiap langkah untuk memastikan bahwa fungsi tujuan dioptimalkan. Algoritme Greedy hanya memiliki satu kesempatan untuk menghitung solusi optimal sehingga tidak pernah mundur dan membalikkan keputusan.


4

TL; DR: Jawabannya tergantung pada implementasi Anda. Untuk kode pseudo yang Anda posting, ini berfungsi dengan bobot negatif.


Varian Algoritma Dijkstra

Kuncinya adalah ada 3 macam implementasi algoritma Dijkstra , tetapi semua jawaban di bawah pertanyaan ini mengabaikan perbedaan di antara varian-varian ini.

  1. Menggunakan -loop bersarangfor untuk melemaskan simpul. Ini adalah cara termudah untuk mengimplementasikan algoritma Dijkstra. Kompleksitas waktu adalah O (V ^ 2).
  2. Implementasi berbasis antrean prioritas / tumpukan + TIDAK boleh masuk kembali, di mana masuk kembali berarti simpul yang santai dapat didorong ke antrean-prioritas lagi untuk dilonggarkan lagi nanti .
  3. Penerapan berbasis antrean / tumpukan prioritas + masuk kembali diizinkan.

Versi 1 & 2 akan gagal pada grafik dengan bobot negatif (jika Anda mendapatkan jawaban yang benar dalam kasus seperti itu, itu hanya kebetulan), tetapi versi 3 masih berfungsi .

Kode pseudo yang diposting di bawah masalah asli adalah versi 3 di atas, sehingga berfungsi dengan bobot negatif.

Berikut adalah referensi yang bagus dari Algoritma (edisi ke-4) , yang mengatakan (dan berisi implementasi java versi 2 & 3 yang saya sebutkan di atas):

T. Apakah algoritma Dijkstra bekerja dengan bobot negatif?

A. Ya dan tidak. Ada dua algoritme jalur terpendek yang dikenal sebagai algoritme Dijkstra, bergantung pada apakah sebuah simpul dapat diantrekan pada antrean prioritas lebih dari sekali. Jika bobotnya bukan negatif, kedua versi tersebut bertepatan (karena tidak ada simpul yang akan diantrekan lebih dari satu kali). Versi yang diimplementasikan di DijkstraSP.java (yang memungkinkan sebuah simpul untuk diantrekan lebih dari satu kali) benar dengan adanya bobot tepi negatif (tetapi tidak ada siklus negatif) tetapi waktu berjalannya eksponensial dalam kasus terburuk. (Kami mencatat bahwa DijkstraSP.java melontarkan pengecualian jika digraf edge-weighted memiliki edge dengan bobot negatif, sehingga programmer tidak terkejut dengan perilaku eksponensial ini.) Jika kita memodifikasi DijkstraSP.java sehingga simpul tidak dapat diantrekan lebih dari sekali (misalnya, menggunakan larik [] yang ditandai untuk menandai simpul yang telah dikendurkan),


Untuk detail implementasi lebih lanjut dan koneksi versi 3 dengan algoritma Bellman-Ford, silakan lihat jawaban ini dari zhihu . Itu juga jawaban saya (tapi dalam bahasa Mandarin). Saat ini saya tidak punya waktu untuk menerjemahkannya ke dalam bahasa Inggris. Saya sangat menghargai jika seseorang dapat melakukan ini dan mengedit jawaban ini di stackoverflow.


1

Pertimbangkan apa yang terjadi jika Anda bolak-balik antara B dan C ... voila

(hanya relevan jika grafik tidak diarahkan)

Diedit: Saya yakin masalahnya ada hubungannya dengan fakta bahwa jalur dengan AC * hanya bisa lebih baik daripada AB dengan adanya tepi berbobot negatif, jadi tidak masalah ke mana Anda pergi setelah AC, dengan asumsi non- tepi berbobot negatif tidak mungkin menemukan jalur yang lebih baik daripada AB setelah Anda memilih untuk mencapai B setelah pergi AC.


ini tidak mungkin, grafik diarahkan.
amit

@ amit: poin bagus, saya melewatkan itu. Saatnya untuk mempertimbangkan kembali masalahnya
prusswan

1

"2) Dapatkah kita menggunakan algoritme Dijksra untuk jalur terpendek untuk grafik dengan bobot negatif - satu gagasan dapat berupa, hitung nilai bobot minimum, tambahkan nilai positif (sama dengan nilai absolut dari nilai bobot minimum) ke semua bobot dan jalankan algoritme Dijksra untuk grafik yang dimodifikasi. Apakah algoritme ini akan berfungsi? "

Ini sama sekali tidak berfungsi kecuali semua jalur terpendek memiliki panjang yang sama. Misalnya diberi jalur terpendek dengan panjang dua sisi, dan setelah menambahkan nilai absolut ke setiap sisi, maka total biaya jalur dinaikkan sebesar 2 * | bobot negatif maks |. Di sisi lain jalur lain dengan panjang tiga sisi, sehingga biaya jalur meningkat 3 * | bobot negatif maks |. Oleh karena itu, semua jalur yang berbeda ditingkatkan dengan jumlah yang berbeda.


0

Anda dapat menggunakan algoritme dijkstra dengan tepi negatif tidak termasuk siklus negatif, tetapi Anda harus mengizinkan simpul dapat dikunjungi beberapa kali dan versi itu akan kehilangan kerumitan waktu cepatnya.

Dalam hal ini secara praktis saya telah melihat lebih baik menggunakan algoritma SPFA yang memiliki antrian normal dan dapat menangani sisi negatif.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.