如何修改MATLAB isdag函数或自定义实现以忽略零长度环?
作业车间调度图的正权环检测问题
问题背景
我在开发作业车间调度优化算法,用有向图建模:节点代表事件,边代表时间优先约束(时间不等式)。比如A到B的长度为2的有向边表示t_B - t_A ≥2,等式用一对反向的正负长度边表示,所以图里会存在零长度环。现在有两个问题:
- 能不能修改MATLAB的
isdag函数,让它忽略零长度环? - 如果不行,该怎么实现正权环的检测逻辑?
我自己写了hasCycleWithPositiveWeight函数,但测试邻接矩阵adjMatrix = [0, 10, 0; -9, 0, 5; 0, 0, 0]时,这个环的总长度是1,应该返回true,但实际返回false,代码如下:
function result = hasCycleWithPositiveWeight(adjMatrix) n = size(adjMatrix,1); visited = false(1,n); path = zeros(1,n); result = false; for i = 1:n pathStart = 1; pathEnd = 1; path(pathEnd) = i; totalWeight = 0; while pathStart <= pathEnd node = path(pathStart); visited(node) = true; for j = 1:n if adjMatrix(node,j) > 0 totalWeight = totalWeight + adjMatrix(node,j); if visited(j) if j == i && totalWeight > 0 result = true; return; end else pathEnd = pathEnd + 1; path(pathEnd) = j; end end end pathStart = pathStart + 1; totalWeight = totalWeight - adjMatrix(node, path(max(pathStart-1,1))); visited(node) = false; end end end
问题解答
能不能修改isdag函数?
不行。isdag是MATLAB的内置编译函数,没法直接修改它的代码。而且它的核心逻辑是检测所有环,不管权重大小,根本不适合用来忽略零长度环,得自己写专门的检测逻辑。
你的代码为啥不对?
你的代码有三个致命问题:
- 只处理正权边:测试用例里的环是
1→2→1,其中包含2→1的负权边(-9),但你的代码只遍历adjMatrix(node,j) > 0的边,直接跳过了这条负权边,自然找不到环。 - 权重计算逻辑混乱:回溯时的权重减法完全不符合路径累积规则,比如
totalWeight = totalWeight - adjMatrix(node, path(max(pathStart-1,1)))这行,根本不知道你想减的是什么权重,导致路径总权重计算错误。 - 环的判定太局限:你只检查回到起始节点
i的情况,但环可能出现在路径中间的节点;而且visited标记在回溯时直接设为false,会导致无法正确识别已经走过的节点,漏判环。
正确的实现思路:用Bellman-Ford算法
检测正权环最靠谱的方法是Bellman-Ford算法(原本用于检测负权环,改一下逻辑就能用在正权环上):
- 初始化所有节点的距离为负无穷,把起始节点的距离设为0。
- 对所有边做
n-1次松弛操作(n是节点数),每次尝试更新节点的最大可达权重。 - 第
n次遍历所有边,如果还能更新某个节点的距离,说明存在正权环(因为正常情况下n-1次松弛就能找到所有最长路径,第n次还能更新就意味着有环可以无限增大权重)。
修复后的代码
function result = hasCycleWithPositiveWeight(adjMatrix) n = size(adjMatrix, 1); dist = -inf(1, n); % 每个节点都当起点测一遍,避免图不连通漏判 for startNode = 1:n dist(startNode) = 0; % 先做n-1次松弛 for i = 1:n-1 updated = false; for u = 1:n for v = 1:n weight = adjMatrix(u, v); % 只有边存在、起点可达,且当前路径权重能更新终点时才操作 if weight ~= 0 && dist(u) ~= -inf && dist(v) < dist(u) + weight dist(v) = dist(u) + weight; updated = true; end end end if ~updated break; % 没有更新,提前结束循环 end end % 第n次检测,能更新就说明有正权环 for u = 1:n for v = 1:n weight = adjMatrix(u, v); if weight ~= 0 && dist(u) ~= -inf && dist(v) < dist(u) + weight result = true; return; end end end % 重置距离数组,测下一个起点 dist = -inf(1, n); end result = false; end
测试你的用例:
adjMatrix = [0, 10, 0; -9, 0, 5; 0, 0, 0]; hasCycleWithPositiveWeight(adjMatrix) % 返回true
补充说明
如果你的需求是忽略所有零权环(不管正负,只要总权重为0就不算),可以调整判断逻辑,但上面的代码正好匹配你检测正权环的需求。
内容的提问来源于stack exchange,提问作者Fer
相关产品推荐
相关产品推荐

