让Ai写个拓扑排序C#算法
前言
使用AI的一个有趣例子
有个业务,在实现过程中需要使用拓扑算法,但我以前所学的东西都还给老师了,刚好这几天公司开展了使用AI的培训与分享,遂想了下,那就让AI来搞下吧。
源代码
Learning-Case/TopologyAlgorithm at main · qiqiqiyaya/Learning-Case
出场AI工具
过程
首先使用的豆包(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#版拓扑排序总结
语雀地址 拓扑排序
本文作者:youliCC
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步