不积跬步无以至千里,不积小流无以成江河。|

youliCC

园龄:5年8个月粉丝:8关注:36

让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 @   youliCC  阅读(8)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起