让Ai写个拓扑排序C#算法

前言

  使用AI的一个有趣例子

  有个业务,在实现过程中需要使用拓扑算法,但我以前所学的东西都还给老师了,刚好这几天公司开展了使用AI的培训与分享,遂想了下,那就让AI来搞下吧。

源代码

  Learning-Case/TopologyAlgorithm at main · qiqiqiyaya/Learning-Case

出场AI工具

  豆包

  DeepSeek

过程

  首先使用的豆包(DeepSeek:服务器繁忙,请稍后再试。)。

  我问:C# 例: A对象依赖B对象的结果,B对象依赖C对象的结果... 该如何排序有序执行。

  豆包回答:

    方法一:使用同步方法按顺序调用(和 使用异步方法按顺序调用,调用方式一样)

        C c = new C();
        B b = new B(c);
        A a = new A(b);

  我想,应该是我问的太简单了,遂修改后问:在上述情况中,A对象不直接依赖B,且B对象不直接依赖C对象 。 该如何排序有序执行

  豆包回答:

        // 创建对象实例
        C c = new C();
        B b = new B();
        A a = new A();

        // 按 C -> B -> A 的顺序执行
        int cResult = c.GetResult();
        int bResult = b.GetResult(cResult);
        int finalResult = a.GetResult(bResult);

  这不是我要的答案,看来还是要直接说出我的直接目的,我问:上述对象依赖关系是拓扑结构,该如何排序有序执行

  豆包回答:如下图

  

  使用 Kahn 算法来实现拓扑排序,使用深度优先搜索进行拓扑排序并执行对象操作,如下代码块

使用深度优先搜索进行拓扑排序并执行对象操作
 using System;
using System.Collections.Generic;

// 定义对象类
class MyObject
{
    public int Id { get; }
    public Func<int> Action { get; }

    public MyObject(int id, Func<int> action)
    {
        Id = id;
        Action = action;
    }
}

class TopologicalSorter
{
    // 深度优先搜索辅助方法
    private static void DFS(int node, Dictionary<int, List<int>> graph, bool[] visited, Stack<int> stack)
    {
        visited[node] = true;

        if (graph.ContainsKey(node))
        {
            foreach (var neighbor in graph[node])
            {
                if (!visited[neighbor])
                {
                    DFS(neighbor, graph, visited, stack);
                }
            }
        }

        stack.Push(node);
    }

    // 使用深度优先搜索进行拓扑排序并执行对象操作
    public static List<int> SortAndExecute(List<MyObject> objects, Dictionary<int, List<int>> dependencies)
    {
        int n = objects.Count;
        bool[] visited = new bool[n + 1];
        Stack<int> stack = new Stack<int>();
        Dictionary<int, MyObject> objectMap = new Dictionary<int, MyObject>();
        foreach (var obj in objects)
        {
            objectMap[obj.Id] = obj;
        }

        // 对每个未访问的节点进行 DFS
        for (int i = 1; i <= n; i++)
        {
            if (!visited[i])
            {
                DFS(i, dependencies, visited, stack);
            }
        }

        List<int> result = new List<int>();
        while (stack.Count > 0)
        {
            int currentId = stack.Pop();
            MyObject currentObj = objectMap[currentId];
            int output = currentObj.Action();
            result.Add(output);
        }

        return result;
    }
}

class Program
{
    static void Main()
    {
        // 创建对象
        MyObject objC = new MyObject(3, () =>
        {
            Console.WriteLine("C 对象正在执行操作...");
            return 3;
        });
        MyObject objB = new MyObject(2, () =>
        {
            Console.WriteLine("B 对象正在执行操作...");
            return 2;
        });
        MyObject objA = new MyObject(1, () =>
        {
            Console.WriteLine("A 对象正在执行操作...");
            return 1;
        });

        List<MyObject> objects = new List<MyObject> { objA, objB, objC };

        // 定义依赖关系,键为依赖对象,值为被依赖对象列表
        Dictionary<int, List<int>> dependencies = new Dictionary<int, List<int>>
        {
            { 1, new List<int> { 2, 3 } }, // A 依赖 B 和 C
            { 2, new List<int> { 3 } }    // B 依赖 C
        };

        // 执行拓扑排序并按顺序执行对象操作
        List<int> results = TopologicalSorter.SortAndExecute(objects, dependencies);
        Console.WriteLine("最终执行结果:");
        foreach (var result in results)
        {
            Console.WriteLine(result);
        }
    }
}

   将代码Copy至VS中调试,代码运行完全没问题(我想:这该不会是从哪里Copy来的吧?)。但是实际开发相关业务中可能会出现循环依赖,若出现,我想知道整个循环依赖的信息。

所以我写了个错误的依赖关系,经测试后发现,算法发现循环依赖,但错误如下图,这个错误太抽象了,要是回溯找错误,要吐血。

  

  于是我接着问:结合上下文,上述 Kahn 算法能检测环但不知道具体环相关的信息,抛出异常时附带上环的具体信息

  豆包:如下图

  

  源代码在DouBao-3文件夹下,copy代码放入vs中测试,循环依赖到时发现,但还是无法给出循环依赖时的具体信息。

  我接着问:上一个回答中 FindCycle 函数你帮我测试下,好像有错误

  豆包回答:如下图

  

  源代码在DouBao-4文件夹下copy代码放入vs中测试,循环依赖到时发现,但还是无法给出循环依赖时的具体信息。我很怀疑这个算法是从哪里copy来的,豆包AI推理不出错误

  然后我又问:上一个回答中代码执行后,无法得出环,请重新给出答案

  豆包回答:如下图

  

  

修正后的代码,还是有错误
 using System;
using System.Collections.Generic;

// 定义对象类
class MyObject
{
    public int Id { get; }
    public string Name { get; }
    public Func<int> Action { get; }

    public MyObject(int id, string name, Func<int> action)
    {
        Id = id;
        Name = name;
        Action = action;
    }

    public override string ToString()
    {
        return $"Object {Name} (ID: {Id})";
    }
}

class TopologicalSorter
{
    public static List<int> SortAndExecute(List<MyObject> objects, Dictionary<int, List<int>> dependencies)
    {
        // 存储每个对象的入度
        var inDegree = new Dictionary<int, int>();
        foreach (var obj in objects)
        {
            inDegree[obj.Id] = 0;
        }

        // 计算每个对象的入度
        foreach (var dep in dependencies)
        {
            foreach (var dependentId in dep.Value)
            {
                inDegree[dependentId]++;
            }
        }

        // 存储入度为 0 的对象的队列
        var queue = new Queue<int>();
        foreach (var obj in objects)
        {
            if (inDegree[obj.Id] == 0)
            {
                queue.Enqueue(obj.Id);
            }
        }

        var result = new List<int>();
        var sortedOrder = new List<int>();
        // 记录每个节点的父节点,用于回溯环
        var parentMap = new Dictionary<int, int>();

        while (queue.Count > 0)
        {
            int currentId = queue.Dequeue();
            sortedOrder.Add(currentId);

            // 找到当前对象
            MyObject currentObj = objects.Find(obj => obj.Id == currentId);
            if (currentObj != null)
            {
                Console.WriteLine($"Executing {currentObj}");
                // 执行当前对象的操作
                int output = currentObj.Action();
                result.Add(output);

                // 检查当前对象的依赖对象
                if (dependencies.ContainsKey(currentId))
                {
                    foreach (var dependentId in dependencies[currentId])
                    {
                        // 记录父节点
                        parentMap[dependentId] = currentId;
                        // 减少依赖对象的入度
                        inDegree[dependentId]--;
                        if (inDegree[dependentId] == 0)
                        {
                            queue.Enqueue(dependentId);
                        }
                    }
                }
            }
        }

        // 检查是否存在环
        if (sortedOrder.Count != objects.Count)
        {
            var remainingNodes = new List<int>();
            foreach (var obj in objects)
            {
                if (!sortedOrder.Contains(obj.Id))
                {
                    remainingNodes.Add(obj.Id);
                }
            }

            // 找出一个环
            var cycle = FindCycle(remainingNodes, parentMap);
            var cycleInfo = string.Join(" -> ", cycle);
            throw new InvalidOperationException($"依赖关系中存在环,环的节点顺序为: {cycleInfo}");
        }

        return result;
    }

    private static List<int> FindCycle(List<int> remainingNodes, Dictionary<int, int> parentMap)
    {
        foreach (int startNode in remainingNodes)
        {
            HashSet<int> visited = new HashSet<int>();
            List<int> path = new List<int>();
            int current = startNode;

            while (true)
            {
                if (visited.Contains(current))
                {
                    int cycleStartIndex = path.IndexOf(current);
                    List<int> cycle = path.GetRange(cycleStartIndex, path.Count - cycleStartIndex);
                    cycle.Add(current);
                    return cycle;
                }

                visited.Add(current);
                path.Add(current);

                if (parentMap.ContainsKey(current))
                {
                    current = parentMap[current];
                }
                else
                {
                    break;
                }
            }
        }

        return new List<int>();
    }
}

class Program
{
    static void Main()
    {
        // 创建多个对象
        var objA = new MyObject(1, "A", () =>
        {
            Console.WriteLine("A 对象正在执行操作...");
            return 1;
        });
        var objB = new MyObject(2, "B", () =>
        {
            Console.WriteLine("B 对象正在执行操作...");
            return 2;
        });
        var objC = new MyObject(3, "C", () =>
        {
            Console.WriteLine("C 对象正在执行操作...");
            return 3;
        });
        var objD = new MyObject(4, "D", () =>
        {
            Console.WriteLine("D 对象正在执行操作...");
            return 4;
        });
        var objE = new MyObject(5, "E", () =>
        {
            Console.WriteLine("E 对象正在执行操作...");
            return 5;
        });
        var objF = new MyObject(6, "F", () =>
        {
            Console.WriteLine("F 对象正在执行操作...");
            return 6;
        });

        var objects = new List<MyObject> { objA, objB, objC, objD, objE, objF };

        // 定义包含环的依赖关系
        var dependencies = new Dictionary<int, List<int>>
        {
            { 1, new List<int> { 2 } }, // A 依赖 B
            { 2, new List<int> { 3 } }, // B 依赖 C
            { 3, new List<int> { 1 } }, // C 依赖 A,形成环
            { 4, new List<int> { 5 } }, // D 依赖 E
            { 5, new List<int> { 6 } }  // E 依赖 F
        };

        try
        {
            // 执行拓扑排序并按顺序执行对象操作
            var results = TopologicalSorter.SortAndExecute(objects, dependencies);
            Console.WriteLine("最终执行结果:");
            foreach (var result in results)
            {
                Console.WriteLine(result);
            }
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

  此时我想,估计豆包也给不出什么能够解决的方案了,换DeepSeek试下

  我直接将豆包给的算法Copy给DeepSeek。

  DeepSeek R1思考了好大一串,这里就不展示了,最后给出了算法,DeepSeek使用深度优先搜索(DFS)来检测环,代码如下:

使用深度优先搜索(DFS)来检测环
 using System;
using System.Collections.Generic;
using System.Linq;

class MyObject
{
    public int Id { get; }
    public string Name { get; }
    public Func<int> Action { get; }

    public MyObject(int id, string name, Func<int> action)
    {
        Id = id;
        Name = name;
        Action = action;
    }

    public override string ToString()
    {
        return $"Object {Name} (ID: {Id})";
    }
}

class TopologicalSorter
{
    public static List<int> SortAndExecute(List<MyObject> objects, Dictionary<int, List<int>> dependencies)
    {
        // 构建后继列表和初始化入度
        var successors = new Dictionary<int, List<int>>();
        var inDegree = new Dictionary<int, int>();

        foreach (var obj in objects)
        {
            inDegree[obj.Id] = 0;
            successors[obj.Id] = new List<int>();
        }

        foreach (var dep in dependencies)
        {
            int node = dep.Key;
            foreach (var dependency in dep.Value)
            {
                successors[dependency].Add(node);
                inDegree[node]++;
            }
        }

        var queue = new Queue<int>();
        foreach (var obj in objects)
        {
            if (inDegree[obj.Id] == 0)
            {
                queue.Enqueue(obj.Id);
            }
        }

        var result = new List<int>();
        var sortedOrder = new List<int>();
        var parentMap = new Dictionary<int, int>();

        while (queue.Count > 0)
        {
            int currentId = queue.Dequeue();
            sortedOrder.Add(currentId);

            MyObject currentObj = objects.Find(obj => obj.Id == currentId);
            if (currentObj != null)
            {
                Console.WriteLine($"Executing {currentObj}");
                int output = currentObj.Action();
                result.Add(output);

                foreach (var successor in successors[currentId])
                {
                    parentMap[successor] = currentId;
                    inDegree[successor]--;
                    if (inDegree[successor] == 0)
                    {
                        queue.Enqueue(successor);
                    }
                }
            }
        }

        if (sortedOrder.Count != objects.Count)
        {
            var remainingNodes = objects.Select(obj => obj.Id).Except(sortedOrder).ToList();
            var cycle = FindCycle(remainingNodes, dependencies);
            var cycleInfo = string.Join(" -> ", cycle);
            throw new InvalidOperationException($"依赖关系中存在环,环的节点顺序为: {cycleInfo}");
        }

        return result;
    }

    private static List<int> FindCycle(List<int> remainingNodes, Dictionary<int, List<int>> dependencies)
    {
        var visited = new Dictionary<int, VisitState>();
        var path = new List<int>();

        foreach (var node in remainingNodes)
        {
            if (DFS(node, dependencies, visited, path))
            {
                // 提取环
                int cycleStart = path.LastIndexOf(path[path.Count - 1]);
                return path.Skip(cycleStart).ToList();
            }
        }

        return new List<int>(); // 未找到环,理论上不会发生
    }

    private enum VisitState
    {
        NotVisited,
        Visiting,
        Visited
    };

    private static bool DFS(int node, Dictionary<int, List<int>> dependencies, Dictionary<int, VisitState> visited, List<int> path)
    {
        if (!visited.ContainsKey(node))
            visited[node] = VisitState.NotVisited;

        if (visited[node] == VisitState.Visiting)
        {
            path.Add(node);
            return true;
        }

        if (visited[node] == VisitState.Visited)
            return false;

        visited[node] = VisitState.Visiting;
        path.Add(node);

        if (dependencies.ContainsKey(node))
        {
            foreach (var neighbor in dependencies[node])
            {
                if (DFS(neighbor, dependencies, visited, path))
                    return true;
            }
        }

        visited[node] = VisitState.Visited;
        path.RemoveAt(path.Count - 1);
        return false;
    }
}

class Program
{
    static void Main()
    {
        var objA = new MyObject(1, "A", () => { Console.WriteLine("A 执行"); return 1; });
        var objB = new MyObject(2, "B", () => { Console.WriteLine("B 执行"); return 2; });
        var objC = new MyObject(3, "C", () => { Console.WriteLine("C 执行"); return 3; });
        var objD = new MyObject(4, "D", () => { Console.WriteLine("D 执行"); return 4; });
        var objE = new MyObject(5, "E", () => { Console.WriteLine("E 执行"); return 5; });
        var objF = new MyObject(6, "F", () => { Console.WriteLine("F 执行"); return 6; });

        var objects = new List<MyObject> { objA, objB, objC, objD, objE, objF };

        var dependencies = new Dictionary<int, List<int>>
        {
            { 1, new List<int> { 2 } }, // A依赖B
            { 2, new List<int> { 3 } }, // B依赖C
            { 3, new List<int> { 1 } }, // C依赖A,形成环
            { 4, new List<int> { 5 } }, // D依赖E
            { 5, new List<int> { 6 } }  // E依赖F
        };

        try
        {
            var results = TopologicalSorter.SortAndExecute(objects, dependencies);
            Console.WriteLine("结果:" + string.Join(", ", results));
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

  

  copy进VS调试,还是错误的,遂又问:请测试上述修正后的代码,并找出环

  DeepSeek答:如下图(艹)

  

  

  我又接着问:C# 代码实现一个拓扑算法,给出例子

  DeepSeek答:如下图

  

  

  源代码在,copy进vs测试,还是无法找到环具体信息。

  又问:上一个回答中如果图中存在环,但无法给出环的具体信息,请修改代码后,给出存在环的图中具体环信息

  DeepSeek答:如下图

  

  

  源代码在给出的代码很接近了,不错还是无法得到全部循环依赖错误信息。

  我换了个思路,如果直接使用,DFS算法实现拓扑排序,是不是更好找出循环依赖?于是我就问:请使用DFS算法实现拓扑排序,当存在环时,输出环的具体信息,并给出例子

  DeepSeek答:如下图

  

  

  我服了,TM的到底是有多忙,这几天大白天问,都是这个鸟样。

  那就问豆包吧,问:结合上下文,使用DFS算法实现拓扑排序,当存在环时,输出环的具体信息,并给出例子

  豆包:

  

  运行后,这此算法已经非常接近了。

  我又继续问:请测试上一个回答的代码

  豆包:

  

  豆包回答说代码能运行通过,且输出正确信息,但是我运行还是错误的。

  我接着了一遍,请豆包修改代码,然后重新测试一遍,还是一样,有点小瑕疵。不过算很不错了。

非常接近答案的代码
 using System;
using System.Collections.Generic;

// 定义对象类
class MyObject
{
    public int Id { get; }
    public string Name { get; }
    public Func<int> Action { get; }

    public MyObject(int id, string name, Func<int> action)
    {
        Id = id;
        Name = name;
        Action = action;
    }

    public override string ToString()
    {
        return $"Object {Name} (ID: {Id})";
    }
}

class TopologicalSorter
{
    // 用于标记节点的状态
    enum NodeState
    {
        Unvisited,
        Visiting,
        Visited
    }

    public static List<int> SortAndExecute(List<MyObject> objects, Dictionary<int, List<int>> dependencies)
    {
        var graph = new Dictionary<int, List<int>>();
        foreach (var obj in objects)
        {
            graph[obj.Id] = new List<int>();
        }
        foreach (var dep in dependencies)
        {
            graph[dep.Key] = dep.Value;
        }

        var states = new Dictionary<int, NodeState>();
        foreach (var obj in objects)
        {
            states[obj.Id] = NodeState.Unvisited;
        }

        var result = new List<int>();
        var stack = new Stack<int>();
        var cycle = new List<int>();

        foreach (var obj in objects)
        {
            if (states[obj.Id] == NodeState.Unvisited)
            {
                if (!DFS(obj.Id, graph, states, stack, ref cycle))
                {
                    var cycleInfo = string.Join(" -> ", cycle);
                    throw new InvalidOperationException($"依赖关系中存在环,环的节点顺序为: {cycleInfo}");
                }
            }
        }

        while (stack.Count > 0)
        {
            int id = stack.Pop();
            MyObject obj = objects.Find(o => o.Id == id);
            result.Add(obj.Action());
        }

        return result;
    }

    private static bool DFS(int node, Dictionary<int, List<int>> graph, Dictionary<int, NodeState> states, Stack<int> stack, ref List<int> cycle)
    {
        states[node] = NodeState.Visiting;
        cycle.Add(node);

        if (graph.ContainsKey(node))
        {
            foreach (var neighbor in graph[node])
            {
                if (states[neighbor] == NodeState.Visiting)
                {
                    // 发现环,回溯找出环的具体信息
                    int startIndex = cycle.IndexOf(neighbor);
                    if (startIndex != -1)
                    {
                        cycle = cycle.GetRange(startIndex, cycle.Count - startIndex);
                    }
                    return false;
                }
                if (states[neighbor] == NodeState.Unvisited)
                {
                    if (!DFS(neighbor, graph, states, stack, ref cycle))
                    {
                        return false;
                    }
                }
            }
        }

        states[node] = NodeState.Visited;
        stack.Push(node);
        cycle.RemoveAt(cycle.Count - 1);
        return true;
    }
}

class Program
{
    static void Main()
    {
        // 创建多个对象
        var objA = new MyObject(1, "A", () =>
        {
            Console.WriteLine("A 对象正在执行操作...");
            return 1;
        });
        var objB = new MyObject(2, "B", () =>
        {
            Console.WriteLine("B 对象正在执行操作...");
            return 2;
        });
        var objC = new MyObject(3, "C", () =>
        {
            Console.WriteLine("C 对象正在执行操作...");
            return 3;
        });
        var objD = new MyObject(4, "D", () =>
        {
            Console.WriteLine("D 对象正在执行操作...");
            return 4;
        });
        var objE = new MyObject(5, "E", () =>
        {
            Console.WriteLine("E 对象正在执行操作...");
            return 5;
        });
        var objF = new MyObject(6, "F", () =>
        {
            Console.WriteLine("F 对象正在执行操作...");
            return 6;
        });

        var objects = new List<MyObject> { objA, objB, objC, objD, objE, objF };

        // 定义包含环的依赖关系
        var dependencies = new Dictionary<int, List<int>>
        {
            { 1, new List<int> { 2 } }, // A 依赖 B
            { 2, new List<int> { 3 } }, // B 依赖 C
            { 3, new List<int> { 1 } }, // C 依赖 A,形成环
            { 4, new List<int> { 5 } }, // D 依赖 E
            { 5, new List<int> { 6 } }  // E 依赖 F
        };

        try
        {
            // 执行拓扑排序并按顺序执行对象操作
            var results = TopologicalSorter.SortAndExecute(objects, dependencies);
            Console.WriteLine("最终执行结果:");
            foreach (var result in results)
            {
                Console.WriteLine(result);
            }
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

  看来,还是得我自己手动修改代码细节了,添加的代码

  然后给豆包发送:,给修正DFS算法后,发给了豆包。

  最后豆包回答我时,使用了我修改正的DFS算法。

  正确算法对了DFS算法顺序是逆拓扑排序

总结

  AI确实能起到辅助作用,关键点在于,发送给AI的消息要能表达的具体点。至此就结束了。

  源代码 Learning-Case/TopologyAlgorithm at main · qiqiqiyaya/Learning-Case

 

C#版拓扑排序总结

语雀地址 拓扑排序

 

posted @ 2025-02-20 23:39  youliCC  阅读(67)  评论(0)    收藏  举报