Dijkstra Algoritmasını kullanarak negatif ağırlıklar


113

Dijkstra algoritmasının neden negatif ağırlıklarla çalışmayacağını anlamaya çalışıyorum. En Kısa Yollar hakkında bir örnek okurken , aşağıdaki senaryoyu anlamaya çalışıyorum:

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

Web sitesinden:

Kenarların hepsinin soldan sağa yönlendirildiğini varsayarsak, A ile başlarsak, Dijkstra'nın algoritması d (A, A) + uzunluğu (kenar) en aza indiren (A, B) kenarı (A, x) seçecektir. Daha sonra d (A, B) = 2'yi ayarlar ve d (A, y) + d (y, C) 'yi en aza indiren başka bir kenar (y, C) seçer; tek seçenek (A, C) 'dir ve d (A, C) = 3'ü ayarlar. Ama hiçbir zaman toplam uzunluğu 1 olan C yoluyla A'dan B'ye en kısa yolu bulamaz.

Aşağıdaki Dijkstra, d [B] uygulamasını kullanmanın neden güncellenmeyeceğini anlayamıyorum 1(Algoritma C köşesine ulaştığında, B üzerinde bir gevşeme çalıştıracak, d [B] 'nin eşit olduğuna bakın 2ve bu nedenle güncellenir değeri 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)
}

Teşekkürler,

Meir


Negatif kenar ağırlıklarıyla yol bulma genel olarak son derece zordur. Hangi rotayı bulursanız bulun, her zaman üzerinde bir yerde keyfi olarak büyük bir negatif kenar ağırlığına sahip keyfi olarak uzun bir rota olasılığı vardır. NP tamamlanırsa şaşırmam.
Nick Johnson

4
Bu şüpheye sahip olan başka biri için, negatif ağırlık döngüleri olmadığı VERİLEN bir grafikte en kısa yolu bulabilirsiniz. Relax işlevi gerçekten başarılı olduğunda "gerçek" bir değer döndürürse, yukarıdaki algoritma çalışacaktır; bu durumda, bitişik köşe "v", yoksa öncelik kuyruğunda sıraya alınacak veya zaten mevcutsa güncellenecektir. Bu, ziyaret edilen düğümlerin gevşemeye devam ettikçe öncelik sırasına yeniden eklenebileceği anlamına gelir.
goelakash

Yanıtlar:


202

Önerdiğiniz algoritma aslında bu grafikte en kısa yolu bulacaktır, ancak genel olarak tüm grafikleri bulamayacaktır. Örneğin, şu grafiği düşünün:

Grafik şekli

Örneğinizde olduğu gibi kenarların soldan sağa yönlendirildiğini varsayın,

Algoritmanız aşağıdaki gibi çalışacaktır:

  1. İlk olarak, set d(A)etmek zerove diğer mesafeler infinity.
  2. Daha sonra düğümünü dışarı genişletmek Aayarı, d(B)için 1, d(C)için zero, ve d(D)için 99.
  3. Ardından, Cnet bir değişiklik olmadan genişlersiniz .
  4. Sonra genişlersiniz B, bu hiçbir etkisi yoktur.
  5. Son olarak, Ddeğişecek d(B)şekilde genişlersiniz -201.

Bunun sonunda, ancak, bu Bildirim o d(C)hala 0, en kısa yol bile olsa Cuzunluğa sahiptir -200. Algoritmanız bu nedenle bazı durumlarda mesafeleri doğru bir şekilde hesaplayamaz. Dahası, her bir düğümden başlangıç ​​düğümüne nasıl gideceğinizi söyleyen işaretçileri geri depolasanız bile A, yanlış yolu geri almayı Cbitirirsiniz A.


35
Mükemmel cevabınıza eklemek için: Dijkstra'nın açgözlü bir algoritma olması, dar görüşlü seçiminin sebebidir.
blubb

4
Teknik olarak, bu grafikteki tüm yolların, negatif döngü A, D, B, A'nın negatif sonsuzluk maliyetine sahip olduğunu belirtmek isterim.
Nate

2
@ Nate- Açıklığa kavuşturmak için, grafikteki tüm kenarlar soldan sağa yönlendirilmiştir. Yüksek kaliteli ASCII sanatımda oklar yapmak biraz zordu. :-)
templatetypedef

2
Daha önce negatif kenarlı grafikleri görmemiş olanlar için, bu grafiğin, ödediğiniz geçiş ücretini kenar ağırlıklarının verdiği ücretli yollardan oluşan bir ağ olarak yorumlamasını faydalı buluyorum. -300 yolu, bunun yerine size 300 $ verdikleri, geriye doğru çılgın bir ücretli yoldur.
D Coetzee

3
@ SchwitJanwityanujit- Dijkstra'nın algoritması böyle çalışıyor. Algoritma yolları araştırmaz , bunun yerine düğümleri işleyerek çalışır . Her düğüm tam olarak bir kez işlenir, bu nedenle B düğümünü işleyip mesafesinin 1 olduğunu anladığımızda, B düğümünü asla tekrar ziyaret etmeyeceğiz veya mesafesini güncellemeye çalışmayacağız.
templatetypedef

25

Grafikte negatif döngü yoksa, yani toplam ağırlığı sıfırdan küçük olan döngüler varsa, Dijkstra'nın negatif ağırlıklarda bile çalıştığını unutmayın.

Elbette, templatetypedef tarafından yapılan örnekte Dijkstra'nın negatif döngüleri, hatta döngüleri olmasa bile neden başarısız olduğu sorulabilir. Bunun nedeni, algoritmayı hedef düğüme ulaşılır ulaşılmaz tutan başka bir durdurma kriteri kullanmasıdır (veya tüm düğümler bir kez yerleşmişse, bunu tam olarak belirtmemiştir). Negatif ağırlıkların olmadığı bir grafikte bu iyi çalışıyor.

Öncelik kuyruğu (yığın) boş çalıştığında algoritmayı durduran alternatif durdurma kriteri kullanılıyorsa (soruda bu durdurma kriteri de kullanılmıştır), o zaman dijkstra negatif ağırlıkları olan ancak içermeyen grafikler için bile doğru mesafeyi bulacaktır. negatif döngüler.

Bununla birlikte, bu durumda, negatif döngüleri olmayan grafikler için dijkstra'nın asimptotik zaman sınırı kaybolur. Bunun nedeni, negatif ağırlıklar nedeniyle daha iyi bir mesafe bulunduğunda önceden yerleşmiş bir düğümün yığının içine yeniden yerleştirilebilmesidir. Bu özelliğe etiket düzeltme denir.


2. Zamanın neden benim "Bellman-Ford'a daha çok benzeyeceğini" ve üstel (Bellman-Ford'dan daha kötü olan) olmadığını düşündüğünüz açık değil. Somut bir algoritmanız ve aklınızda bir kanıtınız var mı?
Gassa

3
1'e: Dijkstra'nın, bir kuyruk boşaldığında duran belirtilen durdurma kriteri ile tam olarak aynı uygulamasını kullanabildiğiniz için (orijinal sorudaki sözde koda bakın), farklı davranmasına rağmen en kısa yollar için hala dijkstras algoritmasıdır. düğümleri birkaç kez yerleştirme (etiket düzeltme).
infty10000101

1
2'ye: Bu sadece bir tahmindi, bu yüzden onu sileceğim. Bence üstel zamanda haklısın, çünkü katlanarak keşfedilmesi gereken birçok yol var.
infty10000101

11

algoritmanızın hiçbir yerinde S kullanmadınız (onu değiştirmenin yanı sıra). dijkstra fikri, bir tepe noktası S üzerindeyken olur, bir daha değiştirilmeyecektir. bu durumda, B, S'nin içine girdiğinde, ona C aracılığıyla bir daha ulaşamazsınız

bu gerçek O (E + VlogV) 'nin karmaşıklığını sağlar [aksi takdirde, kenarları bir defadan fazla ve köşeleri bir defadan fazla tekrarlarsınız]

başka bir deyişle, gönderdiğiniz algoritma, dijkstra algoritmasının vaat ettiği gibi O (E + VlogV) biçiminde olmayabilir.


Ayrıca, negatif ağırlık kenarları olmadan tepe noktasını değiştirmeye gerek yoktur, bu da yol maliyetlerinin yalnızca tekrarlanan kenarlarla artabileceği varsayımını tamamen
bozar

Bu varsayım tam olarak S'yi kullanmamıza izin veren şeydir ve bir köşe S'de olduğunda 'bilmek', bir daha asla değiştirilmeyecektir.
amit

Son ifadeniz yanlış. Gönderilen algoritma, negatif kenarları olmayan grafiklerde çalıştığında zaman karmaşıklığına O (E + VlogV) sahiptir. Bir düğümü daha önce ziyaret ettiğimizi kontrol etmeye gerek yoktur, çünkü ziyaret edilmiş olması gevşeme prosedürünün kuyruğa bir kez daha eklemeyeceğini garanti eder.
Pixar

7

Dijkstra Açgözlü bir yaklaşım olduğundan, bir tepe noktası bu döngü için ziyaret edildi olarak işaretlendiğinde, daha sonra ulaşmak için daha az maliyetli başka bir yol olsa bile, bir daha asla yeniden değerlendirilmeyecektir. Ve böyle bir sorun yalnızca grafikte negatif kenarlar olduğunda ortaya çıkabilir.


Bir açgözlü algoritma , adından da anlaşılacağı gibi, her zaman o anda iyi gibi görünüyor seçim yapar. Belirli bir noktada optimize edilmesi (maksimize edilmesi veya küçültülmesi) gereken bir objektif fonksiyonunuz olduğunu varsayın. Açgözlü bir algoritma , amaç işlevinin optimize edilmesini sağlamak için her adımda açgözlü seçimler yapar . Açgözlü algoritmanın, en iyi çözümü hesaplamak için tek bir şansı vardır, böylece asla geri dönmez ve kararı tersine çevirir.


4

TL; DR: Cevap, uygulamanıza bağlıdır. Gönderdiğiniz sözde kod için, negatif ağırlıklarla çalışır.


Dijkstra Algoritmasının Çeşitleri

Anahtar, Dijkstra algoritmasının 3 tür uygulaması olmasıdır , ancak bu sorunun altındaki tüm cevaplar, bu varyantlar arasındaki farklılıkları görmezden gelir.

  1. Köşeleri gevşetmek için iç içe geçmiş bir fordöngü kullanma . Dijkstra'nın algoritmasını uygulamanın en kolay yolu budur. Zaman karmaşıklığı O (V ^ 2).
  2. Öncelik kuyruğu / yığın tabanlı uygulama + Yeniden girişe izin verilmiyor, burada yeniden giriş gevşetilmiş bir tepe noktasının daha sonra tekrar gevşetilmek üzere öncelik kuyruğuna tekrar itilebileceği anlamına geliyor .
  3. Öncelik kuyruğu / yığın tabanlı uygulama + yeniden girişe izin verilir.

Sürüm 1 ve 2, negatif ağırlıklı grafiklerde başarısız olur (bu gibi durumlarda doğru yanıtı alırsanız, bu sadece bir tesadüftür), ancak sürüm 3 hala çalışmaktadır .

Orijinal sorunun altında yayınlanan sözde kod yukarıdaki sürüm 3'tür, bu nedenle negatif ağırlıklarla çalışır.

İşte Algorithm'den (4. baskı) iyi bir referans: (ve yukarıda bahsettiğim sürüm 2 ve 3'ün java uygulamasını içerir):

S. Dijkstra'nın algoritması negatif ağırlıklarla çalışıyor mu?

A. Evet ve hayır. Dijkstra algoritması olarak bilinen en kısa iki yol algoritması, bir köşe noktasının öncelik kuyruğunda birden fazla sıraya dizilip sıralanamayacağına bağlı olarak vardır. Ağırlıklar negatif olmadığında, iki versiyon çakışır (çünkü hiçbir köşe birden fazla sıralanmayacaktır). DijkstraSP.java'da uygulanan sürüm (bir tepe noktasının birden fazla sıralanmasına izin verir), negatif kenar ağırlıkları varlığında (ancak negatif döngü yok) doğrudur, ancak çalışma süresi en kötü durumda üsteldir. (DijkstraSP.java'nın, kenar ağırlıklı digrafın negatif ağırlıklı bir kenarı varsa, programcı bu üstel davranışa şaşırmaması için bir istisna atar.) DijkstraSP.java'yı bir tepe noktası sıraya alınamayacak şekilde değiştirirsek birden fazla kez (örneğin, gevşetilmiş köşeleri işaretlemek için işaretli bir [] dizi kullanarak),


Daha fazla uygulama detayı ve sürüm 3'ün Bellman-Ford algoritması ile bağlantısı için, lütfen zhihu'daki bu yanıta bakın . Aynı zamanda cevabım (ama Çince). Şu anda onu İngilizceye çevirecek vaktim yok. Birisi bunu yapabilir ve bu yanıtı stackoverflow'da düzenleyebilirse gerçekten minnettarım.


1

B ve C arasında gidip gelirseniz ne olacağını düşünün ... voila

(yalnızca grafik yönlendirilmemişse geçerlidir)

Düzenlendi: Sorunun, AC * ile yolun yalnızca negatif ağırlık kenarlarının varlığıyla AB'den daha iyi olabileceği gerçeğiyle ilgisi olduğuna inanıyorum, bu nedenle AC'den sonra nereye gittiğiniz önemli değil, negatif ağırlık kenarları AC'ye gittikten sonra B'ye ulaşmayı seçtikten sonra AB'den daha iyi bir yol bulmak imkansızdır.


bu mümkün değil, grafik yönlendirilir.
amit

@amit: iyi nokta, bunu kaçırdım. Sorunu yeniden
düşünme

1

"2) Negatif ağırlıklara sahip grafikler için en kısa yollar için Dijksra algoritmasını kullanabilir miyiz - bir fikir, minimum ağırlık değerini hesaplamak, tüm ağırlıklara pozitif bir değer (minimum ağırlık değerinin mutlak değerine eşit) eklemek ve Dijksra algoritmasını çalıştırmak olabilir. değiştirilmiş grafik için. Bu algoritma çalışacak mı? "

En kısa yolların tümü aynı uzunlukta olmadıkça bu kesinlikle işe yaramaz. Örneğin, iki kenarlı en kısa yol verildiğinde ve her kenara mutlak değer eklendikten sonra, toplam yol maliyeti 2 * | maksimum negatif ağırlık | artırılır. Öte yandan, üç kenar uzunluğunda başka bir yol, böylece yol maliyeti 3 * | maks. Negatif ağırlık | artırılır. Bu nedenle, tüm farklı yollar farklı miktarlarda artırılır.


0

Dijkstra'nın algoritmasını negatif döngü içermeyen negatif kenarlarla kullanabilirsiniz, ancak bir köşenin birden çok kez ziyaret edilebilmesine izin vermelisiniz ve bu sürüm hızlı zaman karmaşıklığını kaybedecektir.

Bu durumda, pratik olarak normal kuyruğu olan ve negatif kenarları idare edebilen SPFA algoritmasını kullanmanın daha iyi olduğunu gördüm .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.