Yönlendirilmiş bir grafikteki döngüleri tespit etmek için en iyi algoritma


396

Yönlendirilmiş bir grafikteki tüm döngüleri tespit etmek için en verimli algoritma nedir?

Bir iş bir düğüm ve bağımlılık bir kenar olmak yürütülmesi gereken işleri bir program temsil eden yönlendirilmiş bir grafik var. Döngüsel bağımlılıklara yol açan bu grafikte bir döngü hata durumunu tespit etmek gerekir.


13
Tüm döngüleri tespit etmek istediğinizi söylüyorsunuz, ancak kullanım durumunuz herhangi bir döngü olup olmadığını tespit etmenin yeterli olacağını gösteriyor.
Steve Jessop

29
Tüm döngüleri tespit etmek daha iyi olur, böylece kontrol, düzeltme, kontrol, düzeltme vb.
Yerine

2
Donald B. Johnson'ın "Yönlendirilen bir grafiğin tüm temel devrelerini bulma" başlıklı makaleyi okumalısınız. Sadece temel devreleri bulacaktır, ancak bu sizin durumunuz için yeterli olmalıdır. Ve bu algoritmanın kullanıma hazır Java uygulamam: github.com/1123/johnson
user152468

DFS'yi algoritma için ek değişiklikle çalıştırın: ziyaret ettiğiniz her düğümü işaretleyin. daha önce ziyaret edilmiş olan bir düğümü ziyaret ederseniz, o zaman bir tane var. bir yoldan geri çekildiğinde, ziyaret edilen düğümlerin işaretini kaldırın.
Hesham Yassin

2
@HeshamYassin, daha önce ziyaret ettiğiniz bir düğümü ziyaret ederseniz, mutlaka bir döngü olduğu anlamına gelmez. Lütfen yorumumu okuyun cs.stackexchange.com/questions/9676/… .
Maksim Dmitriev

Yanıtlar:


193

Tarjan en kuvvetli bağlı bileşenler algoritması vardır O(|E| + |V|)zaman karmaşıklığı.

Diğer algoritmalar için Wikipedia'da güçlü bir şekilde bağlı bileşenler konusuna bakın .


69
Güçlü bir şekilde bağlı bileşenleri bulmak size grafikte bulunan döngüleri nasıl anlatır?
Peter

4
Birisi onaylayabilir, ancak Tarjan algoritması A-> A gibi doğrudan kendilerine işaret eden düğüm döngülerini desteklemez.
Cédric Guillemette

24
@Cedrik Doğru, doğrudan değil. Bu Tarjan'ın algoritmasında bir kusur değil, bu soru için nasıl kullanıldığı. Tarjan döngüleri doğrudan bulmaz , güçlü bir şekilde bağlı bileşenler bulur. Elbette, boyutu 1'den büyük olan herhangi bir SCC bir döngü anlamına gelir. Siklik olmayan bileşenler kendi başlarına tekli bir SCC'ye sahiptir. Sorun şu ki, bir öz-döngü kendi başına bir SCC'ye girecektir. Bu yüzden, kendi kendine döngüler için ayrı bir kontrole ihtiyacınız var, ki bu oldukça önemsiz.
mgiuca

13
(grafikteki tüm güçlü bağlı bileşenler)! = (grafikteki tüm döngüler)
optimusfrenk

4
@ aku: Üç renkli bir DFS'nin de çalışma zamanı aynı O(|E| + |V|). Beyaz (hiç ziyaret edilmemiş), gri (geçerli düğüm ziyaret edilir, ancak tüm erişilebilir düğümler henüz ziyaret edilmez) ve siyah (tüm erişilebilir erişilebilir düğümler geçerli olanla birlikte ziyaret edilir) renk kodlaması kullanarak, gri bir düğüm başka bir gri düğüm bulursa ve bir döngü. [Hemen hemen Cormen'in algoritma kitabında ne var]. 'Tarjan'ın algoritması' nın böyle DFS üzerinde herhangi bir faydası olup olmadığını merak ediyorum !!
KGhatak

73

Bunun bir iş programı olduğu göz önüne alındığında, bir noktada bunları önerilen bir yürütme sırasına göre sıralayacağınızdan şüpheleniyorum .

Bu durumda, topolojik sıralama uygulaması her durumda döngüleri algılayabilir. UNIX tsortkesinlikle öyle. Bu nedenle, döngüleri ayrı bir adımda değil, tsorting ile aynı anda tespit etmenin daha verimli olması muhtemel olduğunu düşünüyorum.

Böylece soru, "döngüleri en verimli şekilde nasıl saptarım" yerine "en verimli şekilde nasıl çökerim" haline gelebilir. Hangi cevap büyük olasılıkla "bir kütüphane kullanın", ancak aşağıdaki Wikipedia makalesinde başarısız:

http://en.wikipedia.org/wiki/Topological_sorting

bir algoritma için sözde kod ve Tarjan'dan diğerinin kısa bir açıklamasına sahiptir. Her ikisinin de O(|V| + |E|)zaman karmaşıklığı vardır.


Topolojik sıralama, derinlik ilk arama algoritmasına bağlı olduğu sürece döngüleri algılayabilir, ancak döngüleri gerçekten algılamak için ek defter tutma gerekir. Kurt Peek'in doğru cevabına bakın.
Luke Hutchison

33

Bunu yapmanın en basit yolu , grafiğin ilk derinlik geçişini (DFT) yapmaktır .

Grafiğin nköşeleri varsa, bu bir O(n)zaman karmaşıklığı algoritmasıdır. Her tepe noktasından başlayarak muhtemelen bir DFT yapmanız gerekeceğinden, toplam karmaşıklık olur O(n^2).

İlk derinliği kök düğümü olacak şekilde, geçerli derinlikteki ilk geçişteki tüm köşeleri içeren bir yığın bulundurmalısınız . DFT sırasında zaten yığının içinde olan bir öğeyle karşılaşırsanız, bir döngünüz olur.


21
Bu "normal" bir grafik için doğrudur, ancak yönlendirilmiş bir grafik için yanlıştır . Örneğin, dört düğümü olan "elmas bağımlılık şemasını" düşünün: kenarları B ve C'yi işaret eden ve her biri bir kenarı D'yi işaret eden A. aslında bir döngü - bir döngü olmasına rağmen, bir döngü değildir, çünkü okları takip ederek geçilemez.
Peter

9
@peter, A'dan gelen DFT'nin bir döngü olduğunu yanlış bir şekilde nasıl sonuçlandıracağını açıklayabilir misiniz?
Deepak

10
@Deepak - Aslında, "fizik sihirbazı" nın cevabını yanlış okudum: "yığınında zaten yazıldığını" yazdığı yerde. Bir DFT'nin yürütülmesi sırasında "yığıntaki" dupleri kontrol etmek gerçekten (yönlendirilmiş bir döngüyü tespit etmek için) yeterli olacaktır. Her biriniz için bir oy verin.
Peter

2
O(n)Yığın, zaten ziyaret edilmiş bir düğüm içerip içermediğini görmek için zaman karmaşıklığını neden söylüyorsunuz ? Yığını O(n)taramak, her yeni düğümdeki yığını taraması gerektiğinden çalışma zamanına zaman kazandırır . O(n)Ziyaret edilen düğümleri işaretlerseniz başarabilirsiniz
James Wierzba

Peter'ın dediği gibi, bu yönlendirilmiş grafikler için eksik. Kurt Peek'in doğru cevabına bakın.
Luke Hutchison

32

Cormen ve arkadaşlarının Lemma 22.11'e göre , Algoritmalara Giriş (CLRS):

Yönlendirilmiş bir G grafiği, yalnızca G'nin ilk derinlik araştırması arka kenar vermezse döngüsel değildir.

Bu birkaç cevapta belirtilmiştir; burada CLRS'in 22. bölümüne dayanan bir kod örneği de sunacağım. Örnek grafik aşağıda gösterilmiştir.

resim açıklamasını buraya girin

Önce derinlik araması için CLRS sahte kodunu okur:

resim açıklamasını buraya girin

CLRS Şekil 22.4'teki örnekte, grafik iki DFS ağacından oluşur: biri u , v , x ve y düğümlerinden ve diğeri w ve z düğümlerinden oluşur . Her ağaç bir arka kenarı içerir: gelen bir x için v ve diğer bir z için z (kendi kendine döngü).

Anahtar gerçekleştirilmesi zaman bir arka kenarı olarak karşılaşılan olmasıdır DFS-VISITkomşuları yineleme sırasında, fonksiyon varasında u, bir düğüm ile karşılaşıldığındaGRAY renk.

Aşağıdaki Python kodu, CLRS'in sözde kodunun ifdöngüleri algılayan bir cümle ile uyarlanmasıdır :

import collections


class Graph(object):
    def __init__(self, edges):
        self.edges = edges
        self.adj = Graph._build_adjacency_list(edges)

    @staticmethod
    def _build_adjacency_list(edges):
        adj = collections.defaultdict(list)
        for edge in edges:
            adj[edge[0]].append(edge[1])
        return adj


def dfs(G):
    discovered = set()
    finished = set()

    for u in G.adj:
        if u not in discovered and u not in finished:
            discovered, finished = dfs_visit(G, u, discovered, finished)


def dfs_visit(G, u, discovered, finished):
    discovered.add(u)

    for v in G.adj[u]:
        # Detect cycles
        if v in discovered:
            print(f"Cycle detected: found a back edge from {u} to {v}.")

        # Recurse into DFS tree
        if v not in finished:
            dfs_visit(G, v, discovered, finished)

    discovered.remove(u)
    finished.add(u)

    return discovered, finished


if __name__ == "__main__":
    G = Graph([
        ('u', 'v'),
        ('u', 'x'),
        ('v', 'y'),
        ('w', 'y'),
        ('w', 'z'),
        ('x', 'v'),
        ('y', 'x'),
        ('z', 'z')])

    dfs(G)

Bu örnekte, time yalnızca döngüleri tespit etmekle ilgilendiğimiz için CLRS'deki sözde kodunun yakalanmadığını unutmayın. Ayrıca, kenar listesinden bir grafiğin bitişik liste temsilini oluşturmak için bazı kazan plakası kodu da vardır.

Bu komut dosyası yürütüldüğünde, aşağıdaki çıktıyı yazdırır:

Cycle detected: found a back edge from x to v.
Cycle detected: found a back edge from z to z.

Bunlar CLRS Şekil 22.4'teki örnekteki tam arka kenarlardır.


29

Bir DFS ile başlayın: yalnızca DFS sırasında bir arka kenar bulunursa bir döngü oluşur . Bu, beyaz yol teorumunun bir sonucu olarak kanıtlanmıştır.


3
Evet, aynı düşünüyorum, ama bu yeterli değil, yolumu gönderiyorum cs.stackexchange.com/questions/7216/find-the-simple-cycles-in-a-directed-graph
jonaprieto

Doğru. Ajay Garg sadece bu sorunun kısmi cevabı olan "bir döngü" nin nasıl bulunacağını anlatıyor. Bağlantınız, sorulan soruya göre tüm döngüleri bulmaktan bahsediyor, ancak yine Ajay Garg ile aynı yaklaşımı kullanıyor gibi görünüyor, ancak aynı zamanda tüm olası dfs ağaçlarını da kullanıyor.
Manohar Reddy Poreddy

Bu, yönlendirilmiş grafikler için eksik. Kurt Peek'in doğru cevabına bakın.
Luke Hutchison

26

Bence, yönlendirilmiş bir grafikte döngüyü tespit etmek için en anlaşılır algoritma, grafik boyama algoritmasıdır.

Temel olarak, grafik renklendirme algoritması grafiği DFS biçiminde yürür (Derinlik İlk Arama, başka bir yol keşfetmeden önce bir yolu tamamen araştırdığı anlamına gelir). Bir arka kenar bulduğunda grafiği bir döngü içerdiğini işaretler.

Grafik renklendirme algoritmasının derinlemesine açıklaması için lütfen şu makaleyi okuyun: http://www.geeksforgeeks.org/detect-cycle-direct-graph-using-colors/

Ayrıca, JavaScript'te grafik renklendirme uygulaması sağlıyorum https://github.com/dexcodeinc/graph_algorithm.js/blob/master/graph_algorithm.js


8

Düğümlere "ziyaret edilen" bir özellik ekleyemiyorsanız, bir küme (veya harita) kullanın ve zaten kümede bulunmadıkları sürece ziyaret edilen tüm düğümleri kümeye ekleyin. Benzersiz bir anahtar veya nesnelerin adresini "anahtar" olarak kullanın.

Bu ayrıca, bir kullanıcının sorunu düzeltmesi gerektiğinde kullanışlı olacak döngüsel bağımlılığın "kök" düğümü hakkında bilgi verir.

Başka bir çözüm, yürütülecek bir sonraki bağımlılığı bulmaya çalışmaktır. Bunun için, şu anda nerede olduğunuzu ve daha sonra ne yapmanız gerektiğini hatırlayabileceğiniz bir yığınınız olmalıdır. Yürütmeden önce bu yığın üzerinde bir bağımlılığın olup olmadığını kontrol edin. Eğer öyleyse, bir döngü buldunuz.

Bu, O (N * M) karmaşıklığına sahip gibi görünse de, yığının çok sınırlı bir derinliğe sahip olduğunu (bu nedenle N küçüktür) ve "yürütülen" artı olarak kontrol edebileceğiniz her bağımlılıkla M'nin küçüldüğünü hatırlamanız gerekir. (böylece bir yaprak bulduğunda aramayı durdurabilir asla - çok> M küçük olacaktır, her düğüm kontrol etmek gerekir).

MetaMake'de grafiği bir liste listesi olarak oluşturdum ve daha sonra doğal olarak arama hacmini azaltan her düğümü sildim. Asla bağımsız bir kontrol yapmak zorunda kalmadım, hepsi normal uygulama sırasında otomatik olarak gerçekleşti.

"Sadece test" moduna ihtiyacınız varsa, gerçek işlerin yürütülmesini devre dışı bırakan bir "kuru çalışma" bayrağı eklemeniz yeterlidir.


7

Polinom zamanında yönlendirilmiş bir grafikteki tüm döngüleri bulabilen bir algoritma yoktur. Yönlendirilen grafiğin düğümleri olduğunu ve her düğüm çiftinin birbiriyle bağlantıları olduğunu, yani tam bir grafiğiniz olduğunu varsayalım. Dolayısıyla, bu düğümlerin boş olmayan alt kümeleri bir döngüyü gösterir ve bu tür alt kümelerin 2 ^ n-1 sayısı vardır. Dolayısıyla, polinom zaman algoritması yoktur. Bu nedenle, bir grafikteki yönlendirilmiş döngü sayısını söyleyebilen verimli (aptal olmayan) bir algoritmaya sahip olduğunuzu varsayalım, önce güçlü bağlı bileşenleri bulabilir, ardından algoritmanızı bu bağlı bileşenlere uygulayabilirsiniz. Döngüler sadece bileşenler içinde var olduğundan, aralarında değil.


1
Doğru, düğüm sayısı girişin boyutu olarak alınırsa. Çalışma zamanı karmaşıklığını kenar sayısı, hatta döngü sayısı veya bu önlemlerin bir kombinasyonu olarak da tanımlayabilirsiniz. Donald B. Johnson tarafından "Yönlendirilen bir grafiğin tüm temel devrelerini bulma" algoritması, O ((n + e) ​​(c + 1)) tarafından verilen polinom çalışma süresine sahiptir, burada n düğüm sayısıdır, e kenar sayısı ve c grafiğin temel devre sayısı. İşte bu algoritmanın Java uygulamam: github.com/1123/johnson .
user152468

4

Bu sorunu sml (zorunlu programlama) uygulamıştı. İşte taslak. 0 veya bağımsız bir değere sahip tüm düğümleri bulun. Bu düğümler bir döngünün parçası olamaz (bu yüzden onları kaldırın). Ardından, bu düğümlerden gelen veya giden tüm kenarları kaldırın. Bu işlemi ortaya çıkan grafiğe tekrar tekrar uygulayın. Sonunda herhangi bir düğüm veya kenar kalmazsa, grafiğin herhangi bir döngüsü yoktur, aksi takdirde vardır.


2

Bunu yapmanın yolu, ziyaret edilen köşe sayısını sayarak bir Topolojik Sıralama yapmaktır. Bu sayı, DAG'daki toplam köşe sayısından azsa, bir döngünüz olur.


4
Mantıklı değil. Grafiğin döngüleri varsa, topolojik sıralama yoktur, yani topolojik sıralama için herhangi bir doğru algoritma iptal edilir.
sleske

4
wikipedia'dan: Birçok topolojik sıralama algoritması da döngüleri tespit edecektir, çünkü bunlar topolojik düzenin var olması için engellerdir.
Oleg Mikheev

1
@OlegMikheev Evet, ama Steve diyor ki "Bu sayı DAG'daki toplam köşe sayısından azsa, bir döngü var", bu mantıklı değil.
nbro

@nbro Bahse girerim, topolojik sıralama olmadığında (ve sonra tüm köşeleri ziyaret etmediklerinde) iptal edilen bir topolojik sıralama algoritması varyantı anlamına gelir.
maaartinus

Döngü içeren bir grafikte topolojik sıralama yaparsanız, en az sayıda bozuk kenar içeren bir siparişle karşılaşırsınız (sipariş numarası> sipariş numarası komşu). Ancak sıralamadan sonra, bir grafiği bir döngü ile tespit eden kötü kenarları tespit etmek kolaydır
UGP

2

/mathpro/16393/finding-a-cycle-of-fixed-length Bu çözümü 4 uzunluk için en iyisi :)

Fizik sihirbazı da U (V ^ 2) yapmak zorunda olduğunu söylüyor. Sadece O (V) / O (V + E) 'ye ihtiyacımız olduğuna inanıyorum. Grafik bağlıysa, DFS tüm düğümleri ziyaret edecektir. Grafik alt grafikleri bağladıysa, bu alt grafiğin tepe noktasında bir DFS çalıştırdığımızda, bağlı köşeleri bulacağız ve bunları DFS'nin bir sonraki çalışması için dikkate almayacağız. Bu nedenle, her köşe için çalışma olasılığı yanlıştır.


1

DFS, önceden ziyaret edilmiş bir tepe noktasına işaret eden bir kenar bulursa, orada bir döngünüz vardır.


1
1,2,3: 1,2'de başarısız olur; 1,3; 2,3;
gürültülü kedi

4
@JakeGreene Buraya bakın: i.imgur.com/tEkM5xy.png Anlamak için yeterince basit. Şimdi 0'dan başladığınızı söyleyelim. Sonra düğüm 1'e gidin, oradan başka yol yok, geri dönüş geri döner. Şimdi, daha önce ziyaret edilmiş olan tepe noktasına 1 sahip olan düğüm 2'yi ziyaret edersiniz. Sizce o zaman bir döngü olurdu - ve gerçekten bir tane yok
gürültülü kedi

3
@kittyPL Bu grafik bir döngü içermiyor. Wikipedia'dan: "Yönlendirilmiş bir grafikte yönlendirilmiş bir döngü, aynı tepe noktasında başlayan ve biten bir köşe dizisidir, böylece döngünün birbirini takip eden her iki köşesi için, önceki tepe noktasından diğerine yönlendirilen bir kenar vardır" yönlendirilmiş bir döngü için V'den V'ye giden bir yolu takip edebilmelidir. mafonya'nın çözümü verilen sorun için çalışıyor
Jake Greene

2
@JakeGreene Elbette öyle değil. Algoritmanızı kullanarak ve 1'den başlayarak yine de bir döngü tespit edersiniz ... Bu algoritma sadece kötü ... Genellikle ziyaret edilen bir tepe noktasıyla karşılaştığınızda geriye doğru yürümek yeterlidir.
gürültülü kedi

6
@kittyPL DFS, verilen başlangıç ​​düğümünden döngüleri algılamak için çalışır. Ancak DFS yaparken, çapraz kenarı arka kenardan ayırt etmek için ziyaret edilen düğümleri renklendirmelisiniz. Bir tepe noktasını ilk kez ziyaret ettiğinizde gri olur, ardından tüm kenarları ziyaret edildikten sonra siyaha dönüşürsünüz. DFS yaparken gri bir tepe noktasına vurursanız, bu köşe bir atadır (yani: bir döngünüz vardır). Tepe noktası siyahsa, bu sadece bir kenar çizgisidir.
Kyrra

0

Söylediğiniz gibi, bir dizi işiniz var, bunun belirli bir sırada yürütülmesi gerekiyor. Topological sortişlerin planlanması için gerekli sipariş verildiyse (veya a bağımlılık sorunları varsa direct acyclic graph). dfsBir listeyi çalıştırın ve saklayın ve listenin başında düğüm eklemeye başlayın ve daha önce ziyaret edilmiş bir düğümle karşılaştıysanız. Sonra verilen grafikte bir döngü buldunuz.


-11

Bir grafik bu özelliği karşılıyorsa

|e| > |v| - 1

grafik en azından döngüde içerir.


10
Yönlendirilmemiş grafikler için bu doğru olabilir, ancak yönlendirilmiş grafikler için kesinlikle geçerli değildir.
Hans-Peter Störr

6
Karşı bir örnek A-> B, B-> C, A-> C olabilir.
user152468

Tüm köşelerin kenarları yoktur.
Debanjan Dhar
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.