【问题标题】:Tarjan cycle detection help C#Tarjan 循环检测帮助 C#
【发布时间】:2011-07-10 18:37:24
【问题描述】:

这是 tarjan 循环检测的有效 C# 实现。

算法在这里找到: 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);
            }

        }

注意 DepGraph 只是一个顶点列表。并且 Vertex 有一个代表边缘的其他 Vertex 列表。 index 和 lowlink 也被初始化为 -1

编辑:这是有效的......我只是误解了结果。

【问题讨论】:

  • 为什么是`v.lowlink = Math.Min(v.lowlink, w.index)`而不是v.lowlink = Math.Min(v.lowlink, w.lowlink)
  • @LinMa 无论从技术上来说都是正确的。
  • 此算法不适用于字符串值。我必须在项目及其依赖项中找到循环依赖项。需要进行任何更改才能使其正常工作?

标签: c# algorithm cycle directed-graph tarjans-algorithm


【解决方案1】:

上面说的其实是对的,我没看懂什么是强连通分量。我期待该函数返回一个空的强连接组件列表,但它返回的是单个节点的列表。

我相信上述方法有效。如果您需要,请随时使用!

【讨论】:

  • 问题:在构造传递给 DetectCycle 函数的 DepGraph 时,您不会遇到循环吗?好像你会,如果你这样做了,那么你当时没有检测到周期吗?
  • 您好,发现上述内容很有用,但找不到任何其他已建立的解决方案,所以刚刚将其放入 github 以供其他人查找和贡献:github.com/danielrbradley/CycleDetection希望没关系!
  • 确认工作。我做的有点不同,因为我不想对顶点本身施加副作用(本质上是通过 Vertex 制作一个 index 和 lowValues 的字典),但这非常有效。谢谢!
【解决方案2】:

自 2008 年起,quickgraph 已支持此算法。有关实现,请参见 StronglyConnectedComponentsAlgorithm 类,或使用快捷方式参见 AlgorithmExtensions.StronglyConnectedComponents 方法。

例子:

// 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())

【讨论】:

    【解决方案3】:

    如果有人想快速使用它,上面提供的示例无法正常运行。另请注意,它是基于堆栈的,如果您提供除了最微不足道的图表之外的任何内容,它将引爆您的堆栈。下面是一个带有单元测试的工作示例,它对 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);
        }
    

    我向 Vertex 对象添加了一个 Id 属性,因此很容易查看正在执行的操作,这不是绝对需要的。我也清理了一些代码,作者使用的是页面伪代码命名,便于比较,但信息量不大。

    【讨论】:

    • "Above" 毫无意义,因为答案是随机排序的。最好通过用户名或链接(来自“分享”)来参考具体答案。
    • 我只添加了两个节点,第一个连接到另一个,结果是两个“循环”包含每个节点一次。这不应该是零循环吗?编辑:没关系(“任何不在有向循环上的顶点都自行形成一个强连通分量:例如,入度或出度为0的顶点,或无环图的任何顶点。”)
    • 我在下面由@danielbradley 分叉了这个项目,并由 Roger Hill 添加了这个版本。多谢你们。 github.com/damiensawyer/CycleDetection
    • 哈!刮那个。我刚刚阅读了代码......它仍然使用堆栈。我的复制/粘贴错误!
    猜你喜欢
    • 2015-03-13
    • 1970-01-01
    • 1970-01-01
    • 2010-11-29
    • 2013-07-14
    • 1970-01-01
    • 1970-01-01
    • 2010-09-26
    • 2010-09-23
    相关资源
    最近更新 更多