2011-07-10 35 views
29

Oto działająca implementacja C# wykrywania łańcucha tarjan.Funkcja wykrywania cyklu Tarjana C#

Algorytm znajduje się tutaj: http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm

public class TarjanCycleDetect 
    { 
     private static List<List<Vertex>> StronglyConnectedComponents; 
     private static Stack<Vertex> S; 
     private static int index; 
     private static DepGraph dg; 
     public static List<List<Vertex>> DetectCycle(DepGraph g) 
     { 
      StronglyConnectedComponents = new List<List<Vertex>>(); 
      index = 0; 
      S = new Stack<Vertex>(); 
      dg = g; 
      foreach (Vertex v in g.vertices) 
      { 
       if (v.index < 0) 
       { 
        strongconnect(v); 
       } 
      } 
      return StronglyConnectedComponents; 
     } 

     private static void strongconnect(Vertex v) 
     { 
      v.index = index; 
      v.lowlink = index; 
      index++; 
      S.Push(v); 

      foreach (Vertex w in v.dependencies) 
      { 
       if (w.index < 0) 
       { 
        strongconnect(w); 
        v.lowlink = Math.Min(v.lowlink, w.lowlink); 
       } 
       else if (S.Contains(w)) 
       { 
        v.lowlink = Math.Min(v.lowlink, w.index); 
       } 
      } 

      if (v.lowlink == v.index) 
      { 
       List<Vertex> scc = new List<Vertex>(); 
       Vertex w; 
       do 
       { 
        w = S.Pop(); 
        scc.Add(w); 
       } while (v != w); 
       StronglyConnectedComponents.Add(scc); 
      } 

     } 

Uwaga DepGraph jest tylko lista wierzchołka. i Vertex ma listę innych wierzchołków, które reprezentują krawędzie. Również indeksu i skrótu są inicjowane na -1

EDYCJA: Działa ... Właśnie źle zinterpretowałem wyniki.

+0

Dlaczego to jest 'v.lowlink = Math.Min (v.lowlink, w.index)' inne niż 'v.lowlink = Math.Min (v.lowlink, w.lowlink)'? –

+0

@LinMa Albo jest technicznie poprawne. –

Odpowiedz

9

Powyższe jest poprawne, nie rozumiałem, czym jest silnie połączony komponent. Oczekiwano, że funkcja zwróci pustą listę silnie połączonych komponentów, ale zwróciła listę pojedynczych węzłów.

Wierzę, że powyższe działa. Jeśli chcesz, możesz go użyć!

+0

Pytanie: Czy nie uruchamiasz cykli podczas konstruowania DepGraph, który zostanie przekazany do funkcji DetectCycle? Wygląda na to, że tak, a jeśli tak, to czy nie wykryłeś cyklu w tym czasie? – Joe

+5

Witam, znalazłem powyższe użyteczne i nie mogłem znaleźć żadnych innych ustalonych rozwiązań, więc po prostu uderzyłem go w github dla innych, aby znaleźć i przyczynić się do: https://github.com/danielrbradley/CycleDetection Mam nadzieję, że wszystko w porządku! – danielrbradley

+0

Potwierdzona praca. Zrobiłem to trochę inaczej, ponieważ nie chcę wymuszać efektów ubocznych na samych wierzchołkach (zasadniczo tworząc słownik indeksu i niskich wartości przez Vertex), ale to działało naprawdę dobrze. Dziękuję Ci! – user420667

3

Od 2008 r. Quickgraph obsługuje ten algorytm. Zobacz klasę StronglyConnectedComponentsAlgorithm dla implementacji lub metodę AlgorithmExtensions.StronglyConnectedComponents dla skrótu użytkowania.

Przykład:

// Initialize result dictionary 
IDictionary<string, int> comps = new Dictionary<string, int>(); 

// Run the algorithm 
graph.StronglyConnectedComponents(out comps); 

// Group and filter the dictionary 
var cycles = comps 
    .GroupBy(x => x.Value, x => x.Key) 
    .Where(x => x.Count() > 1) 
    .Select(x => x.ToList()) 
+0

Oto link do quickgraph: http://quickgraph.codeplex.com/ – devinbost

2

Przykład przedstawiony powyżej w pytaniu nie jest funkcjonalny powinien ktoś chce szybko zagrać z nim. Zauważ też, że jest oparty na stosie, który zdetonuje twój stos, jeśli podasz cokolwiek z wyjątkiem banalnych wykresów. Oto przykład pracy z testów jednostkowych, który modeluje wykres prezentowane na stronie wikipedia Tarjan:

public class Vertex 
{ 
    public int Id { get;set; } 
    public int Index { get; set; } 
    public int Lowlink { get; set; } 

    public HashSet<Vertex> Dependencies { get; set; } 

    public Vertex() 
    { 
     Id = -1; 
     Index = -1; 
     Lowlink = -1; 
     Dependencies = new HashSet<Vertex>(); 
    } 

    public override string ToString() 
    { 
     return string.Format("Vertex Id {0}", Id); 
    } 

    public override int GetHashCode() 
    { 
     return Id; 
    } 

    public override bool Equals(object obj) 
    { 
     if (obj == null) 
      return false; 

     Vertex other = obj as Vertex; 

     if (other == null) 
      return false; 

     return Id == other.Id; 
    } 
} 

public class TarjanCycleDetectStack 
{ 
    protected List<List<Vertex>> _StronglyConnectedComponents; 
    protected Stack<Vertex> _Stack; 
    protected int _Index; 

    public List<List<Vertex>> DetectCycle(List<Vertex> graph_nodes) 
    { 
     _StronglyConnectedComponents = new List<List<Vertex>>(); 

     _Index = 0; 
     _Stack = new Stack<Vertex>(); 

     foreach (Vertex v in graph_nodes) 
     { 
      if (v.Index < 0) 
      { 
       StronglyConnect(v); 
      } 
     } 

     return _StronglyConnectedComponents; 
    } 

    private void StronglyConnect(Vertex v) 
    { 
     v.Index = _Index; 
     v.Lowlink = _Index; 

     _Index++; 
     _Stack.Push(v); 

     foreach (Vertex w in v.Dependencies) 
     { 
      if (w.Index < 0) 
      { 
       StronglyConnect(w); 
       v.Lowlink = Math.Min(v.Lowlink, w.Lowlink); 
      } 
      else if (_Stack.Contains(w)) 
      { 
       v.Lowlink = Math.Min(v.Lowlink, w.Index); 
      } 
     } 

     if (v.Lowlink == v.Index) 
     { 
      List<Vertex> cycle = new List<Vertex>(); 
      Vertex w; 

      do 
      { 
       w = _Stack.Pop(); 
       cycle.Add(w); 
      } while (v != w); 

      _StronglyConnectedComponents.Add(cycle); 
     } 
    } 
} 

    [TestMethod()] 
    public void TarjanStackTest() 
    { 
     // tests simple model presented on https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm 
     var graph_nodes = new List<Vertex>(); 

     var v1 = new Vertex() { Id = 1 }; 
     var v2 = new Vertex() { Id = 2 }; 
     var v3 = new Vertex() { Id = 3 }; 
     var v4 = new Vertex() { Id = 4 }; 
     var v5 = new Vertex() { Id = 5 }; 
     var v6 = new Vertex() { Id = 6 }; 
     var v7 = new Vertex() { Id = 7 }; 
     var v8 = new Vertex() { Id = 8 }; 

     v1.Dependencies.Add(v2); 
     v2.Dependencies.Add(v3); 
     v3.Dependencies.Add(v1); 
     v4.Dependencies.Add(v3); 
     v4.Dependencies.Add(v5); 
     v5.Dependencies.Add(v4); 
     v5.Dependencies.Add(v6); 
     v6.Dependencies.Add(v3); 
     v6.Dependencies.Add(v7); 
     v7.Dependencies.Add(v6); 
     v8.Dependencies.Add(v7); 
     v8.Dependencies.Add(v5); 
     v8.Dependencies.Add(v8); 

     graph_nodes.Add(v1); 
     graph_nodes.Add(v2); 
     graph_nodes.Add(v3); 
     graph_nodes.Add(v4); 
     graph_nodes.Add(v5); 
     graph_nodes.Add(v6); 
     graph_nodes.Add(v7); 
     graph_nodes.Add(v8); 

     var tcd = new TarjanCycleDetectStack(); 
     var cycle_list = tcd.DetectCycle(graph_nodes); 

     Assert.IsTrue(cycle_list.Count == 4); 
    } 

Dodałem właściwość identyfikator obiektu Vertex tak łatwo jest zobaczyć, co się robi, to isn” t ściśle potrzebne. Usunąłem trochę kodu, autor używał nazywania z pseudokodu strony, co jest dobre dla porównania, ale nie było zbyt pouczające.

+0

"Powyżej" jest bez znaczenia, jako odpowiedzi sortuj losowo. Lepiej odnieść się do konkretnej odpowiedzi według nazwy lub linku użytkownika (z "udziału"). – Mogsdad

+0

Dodaję tylko dwa węzły, pierwszy połączony z drugim, a wynikiem tego są dwie "pętle" zawierające po jednym węźle. Czy nie powinno to być zerowymi pętlami? Edycja: nevermind ("Każdy wierzchołek, który nie jest w skierowanym cyklu, sam tworzy mocno połączony komponent: na przykład wierzchołek, którego in-degree lub out-degree wynosi 0, lub dowolny wierzchołek acyklicznego wykresu.") –