Grafiklerde döngü bulmak için neden BFS değil de DFS


86

Çoğunlukla DFS, BFS'de değil grafiklerde bir döngü bulmak için kullanılır. Herhangi bir sebep var mı? Her ikisi de, ağacı / grafiği dolaşırken bir düğümün daha önce ziyaret edilip edilmediğini bulabilir.


5
Yönlendirilmiş grafiklerde, bir döngüyü algılamak için yalnızca DFS kullanılabilir; ancak Yönlendirilmemiş grafiklerde her ikisi de kullanılabilir.
Hengameh

Yanıtlar:


74

Önce derinlik araması, daha erken geri dönüş yapabileceğiniz için, kapsamlı ilk aramadan daha fazla bellek verimlidir. Çağrı yığınını kullanırsanız uygulanması da daha kolaydır, ancak bu, yığını aşmayan en uzun yola dayanır.

Ayrıca grafiğiniz yönlendirilmişse , sadece bir düğümü ziyaret edip etmediğinizi değil, aynı zamanda oraya nasıl geldiğinizi de hatırlamanız gerekir. Aksi takdirde bir döngü bulduğunuzu düşünebilirsiniz ama gerçekte sahip olduğunuz tek şey iki ayrı A-> B yoludur, ancak bu bir B-> A yolu olduğu anlamına gelmez. Örneğin,

BFS'yi baştan yaparsanız 0, döngü mevcut olarak algılanır, ancak aslında döngü yoktur.

Derinlikli ilk aramayla, alçalırken düğümleri ziyaret edilmiş olarak işaretleyebilir ve geriye doğru giderken işaretlerini kaldırabilirsiniz. Bu algoritmada bir performans iyileştirmesi için yorumlara bakın.

Yönlendirilmiş bir grafikte döngüleri tespit etmek için en iyi algoritma için Tarjan'ın algoritmasına bakabilirsiniz .


3
(Bellek verimli, çünkü daha erken geri dönüş yaparsınız ve uygulaması daha kolaydır, çünkü yığının açık listeyi açık bir şekilde sürdürmek yerine açık listeyi depolamasına izin verebilirsiniz.)
Amber

3
IMO, sadece kuyruk özyinelemesine güvenirseniz daha kolaydır.
Hank Gay

2
"geri adım atarken işaretlerini kaldırın" - kendi sorumluluğunuzdadır! Bu, kolayca O (n ^ 2) davranışına yol açabilir, özellikle böyle bir DFS, çapraz kenarları "ağaç" kenarları olarak yanlış anlayabilir ("ağaç" kenarları da artık bir ağaç oluşturmayacaklarından yanlış bir isim olacaktır)
Dimitris Andreou

1
@Dimitris Andreo: Performansı artırmak için iki yerine üç ziyaret durumunu kullanabilirsiniz. Yönlendirilmiş grafiklerde, 'Bu düğümü daha önce görmüştüm' ve 'Bu düğüm bir döngünün parçası' arasında bir fark vardır. Yönlendirilmemiş grafiklerle eşdeğerdirler.
Mark Byers

Kesinlikle, üçüncü bir duruma (algoritmayı doğrusal hale getirmek için) kesinlikle ihtiyacınız var, bu yüzden o kısmı gözden geçirmeyi düşünmelisiniz.
Dimitris Andreou

28
  1. DFS'nin uygulanması daha kolaydır
  2. DFS bir döngü bulduğunda, yığın, döngüyü oluşturan düğümleri içerecektir. Aynısı BFS için geçerli değildir, bu nedenle bulunan döngüyü de yazdırmak istiyorsanız fazladan iş yapmanız gerekir. Bu, DFS'yi çok daha kullanışlı hale getirir.

11

Grafik yönlendirilmemişse (döngüleri yönlendirilmiş bir grafikte rapor edecek verimli bir algoritma göstermek için konuğum olun!), Her "çapraz kenarın" bir döngüyü tanımladığı bir BFS makul olabilir. Kesişen kenar ise {v1, v2}ve bu düğümleri içeren kök (BFS ağacında) ise r, döngü r ~ v1 - v2 ~ r( ~bir yoldur, -tek bir uçtur) ve neredeyse DFS'deki kadar kolay raporlanabilir.

Bir BFS kullanmanın tek nedeni, (yönlenmemiş) grafiğinizin uzun yollara ve küçük yol kaplamasına (başka bir deyişle, derin ve dar) sahip olacağını biliyorsanız olabilir. Bu durumda, BFS kuyruğu için DFS yığınına göre orantılı olarak daha az bellek gerektirir (her ikisi de tabii ki doğrusaldır).

Diğer tüm durumlarda, DFS açıkça kazanır. Hem yönlendirilmiş hem de yönlendirilmemiş grafikler üzerinde çalışır ve döngüleri rapor etmek önemsizdir - atadan nesile giden yolun herhangi bir arka kenarını birleştirin ve döngüyü elde edersiniz. Sonuç olarak, bu sorun için BFS'den çok daha iyi ve pratik.


4

BFS, döngüleri bulmada yönlendirilmiş bir grafik için çalışmayacaktır. Bir grafikte A-> B ve A-> C-> B'yi A'dan B'ye yollar olarak düşünün. BFS, B'nin ziyaret edildiği yollardan birini geçtikten sonra söyleyecektir. Bir sonraki yola gitmeye devam ederken, işaretli düğüm B'nin tekrar bulunduğunu söyleyecektir, dolayısıyla orada bir döngü vardır. Açıkçası burada döngü yok.


DFS'nin bu döngünün örneğinizde olmadığını nasıl açıkça belirleyeceğini açıklayabilir misiniz? Verilen örnekte döngünün olmadığını kabul ediyorum ama A-> B'den gidersek ve sonra A-> C-> B'yi bulacağız B'nin zaten ziyaret edildiğini ve ebeveyninin A olmadığını ve DFS'nin, halihazırda ziyaret edilen öğenin ebeveynini şu anda kontrol ettiğimiz mevcut düğümle karşılaştırarak döngüyü algılayacağını okudum. am DFS'yi yanlış mı alıyorum veya ne?
smasher

Burada gösterdiğiniz tek şey, bu özel uygulamanın çalışmadığı, BFS ile imkansız olduğu değil. Aslında, bir daha iş ve uzay alır rağmen, mümkün.
Prune

@Prune: Buradaki tüm konular (sanırım) bfs'nin döngüleri tespit etmek için işe yaramayacağını kanıtlamaya çalışıyor. Nasıl karşı çıkılacağını biliyorsanız, kanıtı vermelisiniz. Basitçe çabaların daha büyük olduğunu söylemek yeterli olmayacak
Aditya Raman

Algoritma bağlantılı gönderilerde verildiği için, ana hatları burada tekrar etmenin uygun olduğunu düşünmüyorum.
Erik

Bağlantılı herhangi bir ilan bulamadım, dolayısıyla aynısını sordum. Bfs yeteneği hakkındaki düşüncenize katılıyorum ve uygulama hakkında biraz düşündüm. İpucu için teşekkürler :)
Aditya Raman

4

Beslememde neden bu kadar eski bir sorunun belirdiğini bilmiyorum ama önceki cevapların hepsi kötü, bu yüzden ...

DFS, çalıştığı için yönlendirilmiş grafiklerde döngüleri bulmak için kullanılır .

DFS'de her köşe, "ziyaret edilir"; burada bir köşe ziyaretinin anlamı:

  1. Köşe başladı
  2. O tepeden ulaşılabilen alt grafik ziyaret edilir. Bu, o tepe noktasından erişilebilen tüm işlenmemiş kenarların izlenmesini ve tüm erişilebilir ziyaret edilmemiş tepe noktalarının ziyaret edilmesini içerir.

  3. Köşe bitti.

Kritik özellik, bir tepe noktasından erişilebilen tüm kenarların, tepe bitmeden önce izlenmesidir. Bu, DFS'nin bir özelliğidir, ancak BFS değildir. Aslında bu, DFS'nin tanımıdır.

Bu özellik nedeniyle, bir döngüdeki ilk tepe noktası başladığında şunu biliyoruz :

  1. Döngüdeki kenarların hiçbiri izlenmedi. Bunu biliyoruz, çünkü onlara yalnızca döngüdeki başka bir tepe noktasından ulaşabilirsiniz ve başlatılacak ilk tepe noktasından bahsediyoruz .
  2. Bu tepe noktasından ulaşılabilen tüm işlenmemiş kenarlar, tamamlanmadan önce izlenecektir ve bu , döngüdeki tüm kenarları da içerecektir , çünkü henüz hiçbiri izlenmemiştir . Bu nedenle, bir döngü varsa, başladıktan sonra, ancak bitmeden önce ilk tepe noktasına geri dönen bir kenar bulacağız; ve
  3. İzlenen tüm kenarlara, başlatılmış ancak tamamlanmamış her köşeden erişilebilir olduğundan, böyle bir tepe noktasına bir kenar bulmak her zaman bir döngüyü gösterir.

Öyleyse, bir döngü varsa, o zaman başlamış ama bitmemiş bir tepe noktasına (2) bir kenar bulmamız garanti edilir ve böyle bir kenar bulursak, o zaman bir döngü (3) olduğu garanti edilir.

Bu nedenle DFS, yönlendirilmiş grafiklerde döngüleri bulmak için kullanılır.

BFS böyle bir garanti vermez, bu yüzden işe yaramaz. (Alt prosedür olarak BFS veya benzerini içeren mükemmel derecede iyi döngü bulma algoritmalarına rağmen)

Öte yandan, yönsüz bir grafik, herhangi bir tepe çifti arasında iki yol olduğunda, yani bir ağaç olmadığında bir döngüye sahiptir. Bunu BFS veya DFS sırasında tespit etmek kolaydır - Yeni tepe noktalarına izlenen kenarlar bir ağaç oluşturur ve diğer herhangi bir kenar bir döngüyü gösterir.


Aslında, asıl nedenleri detaylandıran buradaki en (belki de tek) ilgili cevap budur.
plasmacel

2

Bir ağaçta rastgele bir noktaya bir döngü yerleştirirseniz, DFS, ağacın yaklaşık yarısını kapladığında döngüye çarpma eğiliminde olacaktır ve zamanın yarısı, döngünün gittiği yerden geçmiş olacaktır ve zamanın yarısı geçmeyecektir ( ortalama olarak ağacın geri kalanının yarısında bulacaktır), bu nedenle ağacın ortalama 0,5 * 0,5 + 0,5 * 0,75 = 0,625'ini değerlendirecektir.

Bir ağaçta rastgele bir noktaya bir döngü yerleştirirseniz, BFS, yalnızca ağacın katmanı o derinlikte değerlendirildiğinde döngüye çarpma eğiliminde olacaktır. Bu nedenle, genellikle bir denge ikili ağacın yapraklarını değerlendirmek zorunda kalırsınız, bu da genellikle ağacın daha fazlasını değerlendirmeye neden olur. Özellikle, zamanın 3 / 4'ünde iki bağlantıdan en az biri ağacın yapraklarında görünür ve bu durumlarda ağacın ortalama 3 / 4'ünü (bir bağlantı varsa) veya 7 / Ağacın 8'i (eğer iki tane varsa), yani 1/2 * 3/4 ​​+ 1/4 * 7/8 = (7 + 12) / 32 = 21/32 = arama beklentiniz zaten var 0,656 ... yaprak düğümlerinden uzakta bir döngü eklenmiş bir ağaç arama maliyetini bile eklemeden ağaca.

Ek olarak, DFS'nin uygulanması BFS'den daha kolaydır. Dolayısıyla, döngüleriniz hakkında bir şey bilmediğiniz sürece kullanabileceğiniz tek şey budur (örneğin, çevrimler muhtemelen arama yaptığınız köke yakın olacaktır, bu noktada BFS size bir avantaj sağlar).


Orada bir sürü sihirli sayı var. "DFS daha hızlıdır" argümanlarına katılmıyorum. Tamamen girdiye bağlıdır ve bu durumda hiçbir girdi diğerinden daha yaygın değildir.
IVlad

@Vlad - Sayılar sihirli değil. Bunlar araçlardır, böyle ifade edilirler ve belirttiğim varsayımlar göz önüne alındığında hesaplanması neredeyse önemsizdir. Ortalamaya göre yaklaştırmak kötü bir yaklaşımsa, bu geçerli bir eleştiri olur. (Ve yapı hakkında varsayımlarda bulunursanız cevabın değişebileceğini açıkça belirttim.)
Rex Kerr

sayılar büyülü çünkü hiçbir şey ifade etmiyorlar. DFS'nin daha iyi olduğu bir vakayı ele aldınız ve bu sonuçları genel duruma eklediniz. İfadeleriniz temelsiz: "DFS, ağacın yaklaşık yarısını kapladığında döngüye girme eğiliminde olacaktır": kanıtlayın. Bir ağaçtaki döngüler hakkında konuşamayacağınızdan bahsetmiyorum bile. Bir ağacın tanımı gereği bir döngüsü yoktur. Ne demek istediğini anlamıyorum. DFS, çıkmaz bir noktaya gelene kadar tek yöne gidecektir, bu nedenle ortalama olarak GRAPH'nin (DEĞİL ağaç) ne kadarını keşfedeceğini bilemezsiniz. Az önce hiçbir şeyi kanıtlamayan rastgele bir durum seçtiniz.
IVlad

@Vlad - Döngüsel olmayan tam bağlı yönsüz grafiklerin tümü (köksüz yönsüz) ağaçlardır. "Bir sahte bağlantı için ağaçtan tasarruf edecek bir grafik" demek istedim. Belki de bu, algoritmanın ana uygulaması değildir - belki bazı karışık grafiklerde, onu bir ağaç değil yapan çok sayıda bağlantıya sahip döngüleri bulmak istersiniz. Ancak, ağaç benzeri ise, tüm grafiklerin ortalaması alınmışsa, herhangi bir düğümün söz konusu sahte bağlantının kaynağı olma olasılığı eşittir, bu da bağlantıya ulaşıldığında beklenen ağaç kapsamını% 50 yapar. Bu nedenle, örneğin temsili olmayabileceğini kabul ediyorum. Ancak matematik önemsiz olmalı.
Rex Kerr

1

Bir grafiğin döngüsel olduğunu kanıtlamak için, sadece bir döngüye sahip olduğunu kanıtlamanız gerekir (kenar doğrudan veya dolaylı olarak kendisine işaret eder).

DFS'de her seferinde bir tepe noktası alırız ve döngü olup olmadığını kontrol ederiz. Bir döngü bulunur bulunmaz, diğer köşeleri kontrol etmeyi atlayabiliriz.

BFS'de birçok köşe kenarını eşzamanlı olarak takip etmemiz gerekir ve çoğu zaman sonunda döngü olup olmadığını öğrenirsiniz. Grafiğin boyutu büyüdükçe BFS, DFS'ye kıyasla daha fazla alan, hesaplama ve zaman gerektirir.


0

Özyinelemeli veya yinelemeli uygulamalardan bahsedip bahsetmediğinize bağlıdır.

Özyinelemeli DFS, her düğümü iki kez ziyaret eder. Yinelemeli-BFS, her düğümü bir kez ziyaret eder.

Bir döngüyü tespit etmek istiyorsanız, düğümleri hem bir düğümde "başlattığınızda" hem de bir düğümle "bitirdiğinizde" bitişiklerini eklemeden önce ve sonra araştırmanız gerekir.

Bu, Yinelemeli-BFS'de daha fazla çalışma gerektirir, bu nedenle çoğu kişi Özyinelemeli DFS'yi seçer.

Örneğin std :: stack ile Yinelemeli-DFS'nin basit bir uygulamasının Yinelemeli-BFS ile aynı soruna sahip olduğuna dikkat edin. Bu durumda, bir düğüm üzerinde çalışmayı "bitirdiğinizde" izlemek için yığına kukla öğeler yerleştirmeniz gerekir.

Yinelemeli-DFS'nin bir düğümle ne zaman "bitirdiğinizi" (TopoSort bağlamında yanıtlanır) belirlemek için nasıl ek çalışma gerektirdiğine ilişkin daha fazla ayrıntı için bu yanıta bakın:

Özyineleme olmadan DFS kullanarak topolojik sıralama

Umarım bu, bir düğümü işlemeyi "bitirdiğinizde" belirlemeniz gereken sorunlar için insanların neden Özyinelemeli DFS'yi tercih ettiğini açıklar.


Bu tamamen yanlıştır, çünkü özyineleme kullanmanız veya yinelemeyi yinelemeyle ortadan kaldırmanız önemli değildir. Her düğümü yalnızca bir kez ziyaret eden özyinelemeli bir değişken uygulayabildiğiniz gibi, her düğümü iki kez ziyaret eden yinelemeli bir DFS uygulayabilirsiniz.
plasmacel

0

BFSYönlendirilmiş bir grafikte belirli bir düğümü içeren en kısa döngüyü bulmak istediğinizde kullanmanız gerekecek .

Örneğin:görüntü açıklamasını buraya girin

Verilen düğüm 2 ise, - [2,3,4], [2,3,4,5,6,7,8,9]&' nin parçası olduğu üç döngü vardır [2,5,6,7,8,9]. En kısa[2,3,4]

Bunu BFS kullanarak uygulamak için, uygun veri yapılarını kullanarak ziyaret edilen düğümlerin geçmişini açıkça korumanız gerekir.

Ancak diğer tüm amaçlar için (örneğin: herhangi bir döngüsel yol bulmak veya bir döngünün var olup olmadığını kontrol etmek), DFSdiğerleri tarafından bahsedilen nedenlerden dolayı açık bir seçimdir.

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.