48、图算法:最短路径与节点介数计算

图算法:最短路径与节点介数计算

1. 前置算法:add_predecessor

首先介绍一个简单的算法 add_predecessor ,它的作用是为节点添加前驱节点。以下是该算法的伪代码:

Algorithm 15 add_predecessor()
1: ℓ←preds[j][0]
2: preds[j][ℓ] ←k
3: preds[j][0] ←preds[j][0]+1

这个算法主要完成了将节点 k 添加到节点 j 的前驱节点列表中,并更新了前驱节点列表的长度。

2. 加权图中的最短路径计算

在加权图中寻找最短路径比在无权图中更为复杂。因为在加权图中,节点与其相邻节点之间的距离不一定等于连接它们的边的权重。

例如,有一个包含 N = 6 个节点和 K = 8 条边的加权图。若要计算从节点 1 到其他节点的最短路径,节点 2 到节点 1 的距离 d1,2 = 3 ,对应的最短路径仅包含边 (1, 2) ;而节点 0 到节点 1 的距离 d1,0 = 8 ,最短路径包含两条边 (1, 2) (2, 0) 。如果忽略所有边的权重,将所有边的成本视为 1,使用广度优先搜索(BFS)会将节点对 (1, 2) (1, 0) 的距离都设为 1。

2.1 Dijkstra 算法

Dijkstra 算法可以高效地计算加权图中从一个节点 i 到其他所有节点的最短路径。该算法的伪代码如下:

Algorithm 16 dijkstra()
Input: G (weighted graph), i
Output: distances from i to all the other nodes in G, and list of predecessors
1: for j=0 to N-1 do
2:     dist[j] ←∞
3:     prev[j] ←-1
4: end for
5: dist[i] ←0
6: U ←N
7: while U is not empty do
8:     cur_node ←get_min(U)
9:     U ←U\ {cur_node}
10:    if dist[cur_node] = ∞then
11:        break
12:    end if
13:    for all n in neigh[cur_node]) 0 U do
14:        t_dist ←dist[cur_node] + w[cur_node][n]
15:        if t_dist < dist[n] then
16:            dist[n] ←t_dist
17:            prev[n] ←cur_node
18:            update(U, n)
19:        end if
20:    end for
21: end while
22: return dist, prev

算法步骤如下:
1. 初始化 :将所有节点标记为未访问,存储在集合 U 中。除了起始节点 i 到自身的距离为 0 外,其他节点到 i 的暂定距离设为无穷大。使用数组 dist[] 存储每个节点到 i 的暂定距离,数组 prev[] 存储每个节点的暂定前驱节点。
2. 迭代过程
- 从集合 U 中选择距离 i 最小的节点作为当前节点,将其从 U 中移除并标记为当前状态。
- 对于当前节点的每个未访问邻居 j ,计算经过当前节点到 j 的最短路径的暂定距离 t_dist
- 如果 t_dist 小于 dist[j] ,则更新 dist[j] t_dist ,并将当前节点设为 j 的前驱节点。
- 处理完当前节点的所有邻居后,将当前节点标记为已访问。
3. 终止条件 :当集合 U 为空,或者当前节点到 i 的距离为无穷大时,算法停止。

Dijkstra 算法与广度优先搜索(BFS)有一些重要区别:
- 访问节点方式 :Dijkstra 算法每次迭代只将一个节点(当前节点)添加到已访问节点集合中,而 BFS 会将当前节点的所有未标记邻居添加到标记数组中。
- 节点选择顺序 :Dijkstra 算法需要从集合 U 中按距离起始节点 i 的距离递增顺序选择当前节点,因此需要使用高效的数据结构存储 U

Dijkstra 算法的时间复杂度可以表示为 $O(K · up + N · min)$,其中 up 是更新函数的时间复杂度, min get_min 函数的时间复杂度。不同的 U 实现方式会导致不同的时间复杂度:
| U 的实现方式 | get_min 复杂度 | update 复杂度 | 总时间复杂度 |
| ---- | ---- | ---- | ---- |
| 未排序数组 | $O(N)$ | $O(1)$ | $O(K + N^2)$ |
| 优先队列 | $O(log N)$ | $O(log N)$ | $O((N + K) log N)$ |
| Fibonacci 堆 | - | - | $O(K + N log N)$ |

可以在 www.complex-networks.net 下载实现了 Dijkstra 算法的程序 dijkstra ,该程序可以计算从指定节点到图中其他所有节点的距离和最短路径。

以下是 Dijkstra 算法的流程图:

graph TD;
    A[初始化: 所有节点未访问, dist[i]=0, 其他 dist=∞] --> B{U 是否为空};
    B -- 是 --> C[结束];
    B -- 否 --> D[选择 U 中距离 i 最小的节点 cur_node];
    D --> E[从 U 中移除 cur_node];
    E --> F{dist[cur_node] 是否为 ∞};
    F -- 是 --> C;
    F -- 否 --> G[遍历 cur_node 的未访问邻居 n];
    G --> H[t_dist = dist[cur_node] + w[cur_node][n]];
    H --> I{t_dist < dist[n]?};
    I -- 是 --> J[更新 dist[n]=t_dist, prev[n]=cur_node, 更新 U];
    I -- 否 --> G;
    J --> G;
    G --> K[标记 cur_node 为已访问];
    K --> B;
3. 节点介数计算

节点介数中心性是衡量节点在图中重要性的一个指标。节点 i 的介数中心性可以表示为:
[cB_i = \sum_{j=1,j\neq i}^{N} \sum_{k=1,k\neq i,j}^{N} \delta_{jk}(i)]
其中,(\delta_{jk}(i)) 表示从节点 j 到节点 k 的最短路径中经过节点 i 的路径所占的比例。如果 j k 不相连,则 (\delta_{jk}(i) = 0)。

直接使用上述公式计算图中所有节点的介数中心性,需要计算所有节点对之间的最短路径,并进行两次嵌套求和,时间复杂度较高,为 (O(N^3))。

为了更高效地计算节点介数,Ulrik Brandes 提出了基于以下两个观察的算法:
- Bellman 准则 :节点 i 位于节点 j k 之间的最短路径上,当且仅当 (d_{j,k} = d_{j,i} + d_{i,k})。
- 组合最短路径计数 :从节点 j 到节点 i 的最短路径数量等于从 j i 的每个前驱节点的最短路径数量之和。

基于这两个观察,引入了一些相关的概念和公式:
- 前驱节点集合 :节点 i 在从节点 j i 的最短路径中的前驱节点集合 (P_j(i)) 定义为:
[P_j(i) = {v \in N : (v, i) \in L, d_{j,i} = d_{j,v} + \ell_{vi}}]
其中,(\ell_{vi}) 是边 (v, i) 的长度或成本。
- 组合最短路径计数 :从节点 j 到节点 k 的最短路径数量 (n_{jk}) 可以表示为:
[n_{jk} = \sum_{v \in P_j(k)} n_{jv}]
- 依赖度 :节点 j 对节点 i 的依赖度 (\delta_{j•}(i)) 定义为:
[\delta_{j•}(i) = \sum_{k=1,k\neq i,j}^{N} \delta_{jk}(i)]
- 节点介数 :节点 i 的介数中心性可以表示为:
[cB_i = \sum_{j=1,j\neq i}^{N} \delta_{j•}(i)]

3.1 Brandes 算法

Brandes 算法是对 BFS 算法的一个轻微修改,用于计算无向图中所有节点的介数中心性。以下是该算法的伪代码:

Algorithm 17 brandes()
Input: G
Output: betweenness centrality cB of all the nodes
1: for i = 0 to N-1 do
2:     cB[i] ←0
3: end for
4: for j = 0 to N-1 do
5:     for i = 0 to N-1 do
6:         dist[i] ←N
7:         marked[i] ←N+1
8:         nj[i] ←0
9:         delta[i] ←0
10:    end for
11:    dist[j] ←0
12:    nj[j] ←1
13:    marked[0]←j
14:    d ←0
15:    n ←0
16:    nd ←1
17:    ndp ←0
18:    while d<N and nd >0 do
19:        for i=n to n + nd - 1 do
20:            cur_node ←marked[i]
21:            for all w in neigh[cur_node ] do
22:                if dist[w] = d+1 then
23:                    add_predecessor(w,cur_node,preds)
24:                    nj[w] ←nj[w] + nj[cur_node]
25:                end if
26:                if dist[w] = N then
27:                    dist[w] ←d+1
28:                    add_predecessor(w,cur_node,preds)
29:                    marked[n + nd + ndp] ←w
30:                    ndp ←ndp + 1
31:                    nj[w] ←nj[w] + nj[cur_node]
32:                end if
33:            end for
34:        end for
35:        n ←n + nd
36:        nd ←ndp
37:        ndp ←0
38:        d ←d+1
39:    end while
40:    for k = n - 1 down to 1 do
41:        w ←marked[k]
42:        for all i in predecessors(w) do
43:            delta[i] ←delta[i] + nj[i]/nj[w]∗(1 + delta[w])
44:        end for
45:        cB[w] ←cB[w] + delta[w]
46:    end for
47: end for
48: return cB

该算法的主要步骤如下:
1. 初始化 :将所有节点的介数中心性初始化为 0。
2. 遍历所有节点作为起始节点
- 对于每个起始节点 j ,初始化距离、标记、最短路径数量和依赖度数组。
- 使用类似 BFS 的方法计算从 j 到其他节点的最短路径,并更新最短路径数量。
- 反向遍历标记数组,计算依赖度并更新节点的介数中心性。
3. 返回结果 :返回所有节点的介数中心性。

Brandes 算法在无向图中的时间复杂度为 (O(N(N + K))),在加权图中的时间复杂度与使用 Dijkstra 算法计算所有最短路径的时间复杂度相同,为 (O(N(K + N log N))),都比直接使用公式计算的 (O(N^3)) 复杂度更高效。

可以在 www.complex-networks.net 下载实现了 Brandes 算法的程序 betweenness ,该程序可以计算输入图中所有节点的介数中心性的精确值,也可以基于部分节点的路径进行近似计算。还提供了程序 bet_dependency ,用于计算图中所有节点由于部分节点的最短路径产生的依赖度,可用于并行计算大型图中节点的介数中心性。

图算法:最短路径与节点介数计算(续)

4. 算法复杂度对比

为了更直观地比较不同算法在计算最短路径和节点介数时的性能,我们将相关算法的时间复杂度整理成表格:
| 算法 | 适用图类型 | 计算任务 | 时间复杂度 |
| ---- | ---- | ---- | ---- |
| BFS | 无权图 | 单源最短路径 | (O(N + K)) |
| Dijkstra | 加权图 | 单源最短路径 | (O((N + K) log N))(优先队列实现) |
| 直接计算节点介数 | 无权图/加权图 | 所有节点介数 | (O(N^3)) |
| Brandes | 无权图 | 所有节点介数 | (O(N(N + K))) |
| Brandes | 加权图 | 所有节点介数 | (O(N(K + N log N))) |

从表格中可以看出,在计算单源最短路径时,Dijkstra 算法适用于加权图,而 BFS 适用于无权图。在计算节点介数时,Brandes 算法比直接计算的方法更高效。

5. 算法的实际应用与优化建议
5.1 实际应用
  • 网络路由 :在计算机网络中,Dijkstra 算法可以用于寻找数据包从源节点到目标节点的最短路径,以提高网络传输效率。
  • 社交网络分析 :节点介数中心性可以衡量社交网络中节点的重要性,例如找出社交网络中的关键人物,他们可能在信息传播、影响力扩散等方面起到重要作用。
  • 交通规划 :在城市交通网络中,计算最短路径和节点介数可以帮助规划最优的交通路线,以及确定交通枢纽的重要性。
5.2 优化建议
  • Dijkstra 算法 :可以使用更高效的数据结构,如 Fibonacci 堆来实现集合 U ,将时间复杂度优化到 (O(K + N log N))。
  • Brandes 算法 :对于大型图,可以采用并行计算的方式,将不同起始节点的计算任务分配给多个处理器,以提高计算效率。
6. 总结

本文介绍了图算法中计算最短路径和节点介数的相关内容,主要包括:
1. add_predecessor 算法 :用于为节点添加前驱节点。
2. Dijkstra 算法 :用于计算加权图中从一个节点到其他所有节点的最短路径,与 BFS 算法有明显区别,不同的实现方式会导致不同的时间复杂度。
3. 节点介数计算 :直接计算节点介数的时间复杂度较高,Brandes 算法通过引入 Bellman 准则和组合最短路径计数的方法,提高了计算效率。

通过合理选择算法和优化实现方式,可以在不同的图应用场景中更高效地完成最短路径和节点介数的计算任务。

以下是一个简单的流程图,展示了整个计算过程的大致步骤:

graph LR;
    A[输入图 G] --> B{选择计算任务};
    B -- 最短路径 --> C{图类型};
    C -- 无权图 --> D[BFS 算法];
    C -- 加权图 --> E[Dijkstra 算法];
    B -- 节点介数 --> F{图类型};
    F -- 无权图 --> G[Brandes 算法(无权图)];
    F -- 加权图 --> H[Brandes 算法(加权图)];
    D --> I[输出最短路径结果];
    E --> I;
    G --> J[输出节点介数结果];
    H --> J;

希望这些算法和优化建议能帮助你在实际应用中更好地处理图相关的计算问题。如果你对这些算法有更深入的需求,可以进一步研究相关的文献和代码实现。

内容概要:本文为《科技类企业品牌传播白皮书》,系统阐述了新闻媒体发稿、自媒体博主种草短视频矩阵覆盖三大核心传播策略,并结合“传声港”平台的AI工具资源整合能力,提出适配科技企业的品牌传播解决方案。文章深入分析科技企业传播的特殊性,包括受众圈层化、技术复杂性传播通俗性的矛盾、产品生命周期影响及2024-2025年传播新趋势,强调从“技术输出”向“价值引领”的战略升级。针对三种传播方式,分别从适用场景、操作流程、效果评估、成本效益、风险防控等方面提供详尽指南,并通过平台AI能力实现资源智能匹配、内容精准投放全链路效果追踪,最终构建“信任—种草—曝光”三位一体的传播闭环。; 适合人群:科技类企业品牌市场负责人、公关传播从业者、字营销管理者及初创科技公司创始人;具备一定品牌传播基础,关注效果可量化AI工具赋能的专业人士。; 使用场景及目标:①制定科技产品全生命周期的品牌传播策略;②优化媒体发稿、KOL合作短视频运营的资源配置ROI;③借助AI平台实现传播内容的精准触达、效果监测风险控制;④提升品牌在技术可信度、用户信任市场影响力方面的综合竞争力。; 阅读建议:建议结合传声港平台的实际工具模块(如AI选媒、达人匹配、据驾驶舱)进行对照阅读,重点关注各阶段的标准化流程据指标基准,将理论策略平台实操深度融合,推动品牌传播从经验驱动转向工具双驱动。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值