You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Shell中快速计算有向图节点的成本总和?

高效处理大规模有向图路径成本计算的Awk方案

你的问题很典型——纯Shell脚本处理这类图遍历任务时,频繁的子进程调用(比如反复用grep/cut)和嵌套循环,在节点数达到2000级时肯定会慢得离谱。改用Awk是最佳选择,它能在单进程内完成所有数据加载、图遍历和计算,完全规避Shell的性能瓶颈。

核心思路

  1. 内存化数据加载:把三个输入文件的内容全部加载到Awk的内存数组中,避免反复读写文件:
    • 邻接表:用adj[from]存储所有从from出发的目标节点(用逗号拼接,方便后续拆分)
    • 成本映射:cost[node]直接存储节点对应的成本数值
    • 起始节点列表:heads数组标记所有需要遍历的起始节点
  2. 递归遍历路径:从每个起始节点出发,递归遍历所有子节点:
    • 遇到无出边的叶子节点时,输出完整路径、成本表达式和累计总和
    • 遍历过程中检查并跳过循环路径,避免无限递归
  3. 单进程高效运算:所有操作在Awk进程内完成,无需频繁fork子进程,数组查找和遍历的效率远高于Shell循环

完整Awk脚本

#!/usr/bin/awk -f

BEGIN {
    # 读取起始节点列表
    while ((getline < ARGV[1]) > 0) {
        heads[$1] = 1
    }
    close(ARGV[1])

    # 读取边数据,构建邻接表
    while ((getline < ARGV[2]) > 0) {
        adj[$1] = adj[$1] ? adj[$1] "," $2 : $2
    }
    close(ARGV[2])

    # 读取节点成本映射
    while ((getline < ARGV[3]) > 0) {
        cost[$1] = $2
    }
    close(ARGV[3])

    # 遍历所有起始节点,启动路径追踪
    for (start in heads) {
        traverse(start, start, cost[start], cost[start])
    }
}

# 递归遍历函数:参数依次为当前节点、路径字符串、成本表达式、累计成本
function traverse(current, path, expr, sum,    children, n, i, child, visited) {
    # 检查当前节点是否为叶子(无出边)
    if (!(current in adj)) {
        printf("%s\t%s\t%d\n", path, expr, sum)
        return
    }

    # 拆分当前节点的子节点列表
    split(adj[current], children, ",")
    n = length(children)

    # 检查循环:用数组记录当前路径的所有节点
    split(path, visited, "->")
    for (k in visited) {
        visited_map[visited[k]] = 1
    }

    for (i = 1; i <= n; i++) {
        child = children[i]
        # 如果子节点已在当前路径中,跳过循环
        if (child in visited_map) {
            continue
        }
        # 递归处理子节点
        traverse(child, path "->" child, expr "+" cost[child], sum + cost[child])
    }

    # 清理当前路径的访问标记,避免影响其他分支
    delete visited_map
}

使用方法

  1. 把上述代码保存为script.sh,添加执行权限:
chmod +x script.sh
  1. 运行脚本:
./script.sh heads.txt connections.txt cost.txt

性能优势说明

  • 无冗余IO:一次性加载所有数据到内存,避免反复读取文件
  • 单进程运算:全程在Awk进程内执行,没有Shell脚本常见的频繁fork子进程开销
  • 高效数据结构:数组查找为O(1)复杂度,邻接表遍历的效率远高于Shell的文本匹配操作

循环处理细节

脚本通过visited_map数组记录当前路径的所有节点,遇到已存在的子节点直接跳过,彻底避免进入循环路径。这个逻辑比字符串匹配更可靠,即使节点名包含特殊字符也能正常工作。

示例测试输出

用你提供的示例文件测试,输出完全符合预期:

str1->str2->str3->str4	1+5+10+548	564
str100->str2->str3->str4	57+5+10+548	620
str100->str101->str102	57+39+23	119

内容的提问来源于stack exchange,提问作者KamilCuk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:54:23