图神经网络实战——图论
图神经网络实战——图论
- 0. 前言
- 1. 图属性
- 1.1 有向图和无向图
- 1.2 加权图与非加权图
- 1.3 连通图非连通图
- 1.4 其它图类型
- 2. 图概念
- 2.1 基本对象
- 2.2 图的度量指标
- 2.2 邻接矩阵表示法
- 3. 图算法
- 3.1 广度优先搜索
- 3.2 深度优先搜索
- 小结
- 系列链接
0. 前言
图论 (Graph theory
) 是数学的一个基本分支,涉及对图研究。图是复杂数据结构的可视化表示,有助于理解不同实体之间的关系。图论提供了大量建模和分析现实问题的工具,如交通系统、社交网络和互联网等。
在本节中,将介绍图论的基本原理,主要涉及三个方面:图属性、图概念和图算法。首先,我们将定义图及其组成部分;然后,我们将介绍不同类型的图,并分析它们的属性和应用。接下来,我们将介绍基本的图概念,包括邻接矩阵等;最后,将深入介绍图算法,重点包括广度优先搜索 (breadth-first search
, BFS
) 和深度优先搜索 (depth-first search
, DFS
)。
1. 图属性
在图论中,图 (Graph
) 是一种数学结构,由一组对象(称为顶点或节点)和一组连接顶点对的连接(称为边)组成。使用符号 G = ( V , E ) G = (V,E) G=(V,E) 表示图,其中 G G G 是图、 V = { v 1 , v 2 , . . . , v i , . . . v n } V=\{v_1, v_2,...,v_i,...v_n\} V={v1,v2,...,vi,...vn} 是顶点集、 E { e 1 , e 2 , . . . , e i , . . . e m } E\{e_1, e_2,...,e_i,...e_m\} E{e1,e2,...,ei,...em} 是边集, v i v_i vi表示节点 i i i, e i j = ( v i , v j ) e_{ij}=(v_i,v_j) eij=(vi,vj) 表示连接节点 i i i 和节点 j j j 之间的边。
图中的节点可以代表任何对象,例如城市、人物、网页或分子,而边则代表它们之间的关系或联系,如城市道路、社会关系、超链接或化学键。
1.1 有向图和无向图
如果图中的边都存在方向性,则称这样的边为有向边 e i j = < v i , v j > e_{ij}=<v_i,v_j> eij=<vi,vj>,这意味着边以特定的方向连接两个节点,其中节点 v i v_i vi 是这条有向边的起点,节点 v j v_j vj 是这条有向边的终点,包含有向边的图称为有向图 (directed graph
)。相对应的,无向图 (undirected graph
) 的边是无向的,即边没有方向。这意味着两个顶点之间的边可以朝任意方向遍历,访问节点的顺序并不重要,也可以认为无向边是对称的,同时包含两个方向: e i j = < e i , e j > = e j i = < e j , e i > e_{ij}=<e_i,e_j>=e_{ji}=<e_j,e_i> eij=<ei,ej>=eji=<ej,ei>。
在 Python
中,可以使用 networkx
库的 nx.Graph()
定义无向图:
import networkx as nx
from matplotlib import pyplot as plt
G = nx.Graph()
G.add_edges_from([('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'F'), ('C', 'G')])
nx.draw_networkx(G)
plt.show()
使用 networkx
库创建有向图只需将 nx.Graph()
替换为 nx.DiGraph()
:
DG = nx.DiGraph()
DG.add_edges_from([('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'F'), ('C', 'G')])
nx.draw_networkx(DG)
plt.show()
在有向图中,边通常用箭头表示其方向,如下图所示。
1.2 加权图与非加权图
图的另一个重要属性是边是加权的还是非加权的。在加权图 (weighted graph
) 中,每条边都有相关的权重,一般情况下,我们可以将权重抽象为两个节点之间的连接强度。这些权重可以代表不同性质,如距离、旅行时间或成本。
例如,在一个交通网络中,边的权重可能代表不同城市之间的距离或在这些城市之间旅行所需的时间。相对应的,非加权图 (unweighted graph
) 的边没有权重,这类图通常用于节点间关系为二元关系的情况,边只表示节点间是否存在连接。
可以修改上一小节的无向图,为边添加权重。在 networkx
中,图的边用一个包含起点和终点节点的元组以及一个指定边权重的字典来定义的:
WG = nx.Graph()
WG.add_edges_from([('A', 'B', {"weight": 10}), ('A', 'C', {"weight": 20}), ('B', 'D', {"weight": 30}), ('B', 'E', {"weight": 40}), ('C', 'F', {"weight": 50}), ('C', 'G', {"weight": 60})])
labels = nx.get_edge_attributes(WG, 'weight')
nx.draw_networkx(WG)
nx.draw_networkx_edge_labels(WG, pos=nx.spring_layout(WG), edge_labels=labels)
plt.show()
1.3 连通图非连通图
图的连通性是图论中的一个基本概念,与图的结构和功能密切相关。在连通图 (connected graph
) 中,图中任意两个顶点之间都有一条路径。从形式上看,当且仅当对于图中的每一对顶点 v i v_i vi 和 v j v_j vj,都存在一条从 v i v_i vi 到 v j v_j vj 的路径时,该图才是连通的。 相反,如果一个图不连通,即至少有两个顶点之间没有路径连接(即图中存在孤立的点),则该图是非连通图 (connected graph
)。
networkx
库提供了一个内置函数,用于验证图形是否连通。在以下示例中,第一个图包含孤立节点 (4
和 5
),与第二个图不同:
g1 = nx.Graph()
g1.add_edges_from([(1, 2), (2, 3), (3, 1), (4, 5)])
print(f"Is graph 1 connected? {nx.is_connected(g1)}")
g2 = nx.Graph()
g2.add_edges_from([(1, 2), (2, 3), (3, 1), (1, 4)])
print(f"Is graph 2 connected? {nx.is_connected(g2)}")
plt.subplot(121)
nx.draw_networkx(g1, pos=nx.spring_layout(g1))
plt.subplot(122)
nx.draw_networkx(g2, pos=nx.spring_layout(g2))
plt.show()
代码输出结果如下:
Is graph 1 connected? False
Is graph 2 connected? True
由于节点 4
和 5
的存在,第一个图是非连通的,而第二个图没有孤立节点,因此是连通的。
连通图许多有趣的特性和应用。例如,在通信网络中,连通图可以确保任何两个节点都能相互通信。相反,非连通图中存在孤立的节点,这些节点无法与网络中的其他节点通信,这为设计高效路由算法带来了挑战。
判断图形连通性的方法多种多样。最常见的一种判断方法是,使得图不再连通需要移除的最少边数,称为图的最小割。最小切割问题在网络流量优化、聚类和群落检测方面有多种应用。
1.4 其它图类型
除了常用的图类型外,还有一些具有独特属性和特征的特殊图类型:
- 树 (
Tree
):一种连通的、无向、无循环的图。由于树中任意两个节点之间只有一条路径,因此树是一类特殊的图。树通常用于模拟层次结构,如家族树、组织结构或分类树 - 有根树 (
Rooted tree
):树上有一个节点被指定为根,其他节点都通过唯一的路径与之相连。计算机科学中常用有根树来表示层次数据结构,如文件系统或 XML 文档的结构 - 有向无环图 (
Directed acyclic graph
,DAG
):一种没有循环的有向图,其中边只能沿特定方向遍历,不存在循环。DAG
通常用于模拟任务或事件之间的依赖关系,例如项目管理或计算工作的关键路径 - 二部图 (
bipartite graph
):顶点可分为两个不相交集合的图,所有边都连接不同集合中的顶点。数学和计算机科学中经常使用二部图来模拟两类不同对象之间的关系,如用户和商品、作者和作品 - 完全图 (
complete graph
):每对顶点都由一条边连接的图。在组合学中,完全图常用于模拟涉及成对连接的问题;在计算机网络中,完整图常用于模拟完全连接的网络。
下图展示了上述不同类型的图:
2. 图概念
在本节中,我们将介绍图论中的一些基本概念,包括图对象(如度和邻居)、图度量(如中心性和密度)以及邻接矩阵表示法。
2.1 基本对象
图论中的一个关键概念是节点的度 (degree
),即与该节点相连的边的数量。如果某节点是一条边的端点,则称该边与该节点关联。节点 v v v 的度通常用 d e g ( v ) deg(v) deg(v) 表示:
- 在无向图中,节点的度是与之相连的边的数量。如果节点与自身相连(称为循环或自循环),则度数会增加
2
- 在有向图中,度分为两种:入度 (
indegree
) 和出度 (outdegree
)。节点的入度(用 d e g − ( v ) deg^-(v) deg−(v) 表示)代表指向该节点的边的数量;而出度(用 d e g + ( v ) deg^+(v) deg+(v) 表示)代表从该节点出发的边的数量。在这种情况下,一个自循环会使入度和出度分别增加1
入度和出度对于分析和理解有向图至关重要,因为它们可以帮助我们了解信息或资源在图中的分布情况。例如,入度高的节点可能是重要的信息或资源的重要目的地。相反,出度高的节点可能是信息或资源来源。
在 networkx
中,可以使用内置方法计算节点度、入度或出度:
G = nx.Graph()
G.add_edges_from([('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'F'), ('C', 'G')])
print(f"deg(A) = {G.degree['A']}")
DG = nx.DiGraph()
DG.add_edges_from([('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'F'), ('C', 'G')])
print(f"deg^-(A) = {DG.in_degree['A']}")
print(f"deg^+(A) = {DG.out_degree['A']}")
输出结果如下所示:
deg(A) = 2
deg^-(A) = 0
deg^+(A) = 2
在以上有向图中,节点与两条边相连 ( d e g ( a ) = d e g + ( a ) = 2 deg(a)=deg^+(a)=2 deg(a)=deg+(a)=2),但不是其中任何一条边的目的节点 ( d e g − ( a ) = 0 (deg^-(a)=0 (deg−(a)=0)。
节点邻居的概念与节点度密切相关,如果存在一条边连接 v i v_i vi 和 v j v_j vj,则称 v j v_j vj 是 v i v_i vi 的邻居 (neighbor
),反之亦然。如果两个结点 v i v_i vi 和 v j v_j vj 是边 e e e 的端点,则称 v i v_i vi 与 v j v_j vj 互为邻接点 (adjacent point
)。邻居和邻接的概念是许多图算法的基础,例如在两个节点之间搜索路径或识别网络中的集群。
在图论中,路径 (path
) 是连接图中两个(或多个)节点的边序列。路径的长度是沿路径遍历的边的数量。路径有多种类型,其中以下两种路径尤为重要:
- 简单路径 (
simple path
):不重复经过任何节点(除起点和终点外)的路径 - 循环(
cycle
):首尾节点相同的路径。如果一个图不包含循环(如树和 DAG),则称其为非循环图
度和路径可用于确定节点在网络中的重要性,这种度量被称为中心性 (centrality
)。
2.2 图的度量指标
中心性量化了图中节点的重要性,可以根据节点的连通性以及对图内信息流或互动的影响来识别图中的关键节点。中心度有多种度量方法,每种方法都能从不同角度反映节点的重要性:
- 度中心性 (
Degree centrality
):最简单、最常用的中心性度量方法之一,其定义为节点的度数。度中心性越高,表明节点与图中其他节点的连接程度越高,因此对网络的影响越大 - 接近中心性(
Closeness centrality
):衡量一个节点与图中所有其他节点的接近程度,它相当于目标节点与图中所有其他节点之间最短路径的平均长度。接近中心性高的节点可以快速到达网络中的所有其他节点 - 中介中心性 (
Betweenness centrality
):衡量一个节点位于图中其他节点对之间最短路径上的次数。具有高中介中心性的节点是图中不同部分之间的瓶颈或桥梁
可以使用 networkx
的内置函数计算图中的这些度量指标并分析结果:
print(f"Degree centrality = {nx.degree_centrality(G)}")
print(f"Closeness centrality = {nx.closeness_centrality(G)}")
print(f"Betweenness centrality = {nx.betweenness_centrality(G)}")
输出结果如下所示,字典中包含每个节点的得分:
Degree centrality = {'A': 0.3333333333333333, 'B': 0.5, 'C': 0.5, 'D': 0.16666666666666666, 'E': 0.16666666666666666, 'F': 0.16666666666666666, 'G': 0.16666666666666666}
Closeness centrality = {'A': 0.6, 'B': 0.5454545454545454, 'C': 0.5454545454545454, 'D': 0.375, 'E': 0.375, 'F': 0.375, 'G': 0.375}
Betweenness centrality = {'A': 0.6, 'B': 0.6, 'C': 0.6, 'D': 0.0, 'E': 0.0, 'F': 0.0, 'G': 0.0}
图中节点 A
、B
和 C
的重要性取决于所使用的中心度类型。度中心性认为节点 B
和 C
比节点 A
更重要,因为它们有更多的邻居。而节点 A
、B
和 C
具有相同的中介中心性,因为它们都位于许多其他节点之间的最短路径上。
密度 (density
) 是另一个重要的度量指标,它度量了图的连接程度,是图中实际边数与最大可能边数之间的比值。与密度低的图相比,密度高的图通常连通性更强,信息流动更多。
密度的计算公式取决于图是有向图还是无向图。对于有 n n n 个节点的无向图,最大可能的边数是 n ( n − 1 ) 2 \frac {n(n-1)} 2 2n(n−1);对于有 n n n 个节点的有向图,边的最大可能数量为 n ( n − 1 ) n(n-1) n(n−1)。
图密度的计算方法是边数除以最大边数。例如,下图中的图有 6
条边,最大可能的边数为 7 ( 7 − 1 ) 2 = 21 \frac {7(7 - 1)}2 = 21 27(7−1)=21 条。因此,该图的密度为 6 21 ≈ 0.2857 \frac 6 {21} ≈ 0.2857 216≈0.2857。
稠密图的密度接近 1
,而稀疏图的密度接近 0
。对于稠密图或稀疏图的定义没有严格的规则,但一般来说,如果密度大于 0.5
,则视为稠密图;如果密度小于 0.1
,则视为稀疏图。
2.2 邻接矩阵表示法
邻接矩阵 (adjacency matrix
) A A A 是表示图中边的矩阵,其中每个元素表示两个节点之间是否有边。邻接矩阵是大小为 n × n n\times n n×n 的正方形矩阵,其中 n n n 是图中的节点数量。 A i j A_{ij} Aij 的值为 1
表示节点 i i i 和节点 j j j 之间有一条边,而值为 0
则表示没有边。对于无向图,邻接矩阵沿主对角线对称,而对于有向图,邻接矩阵不一定对称:
A i j = { 1 , ( v i , v j ) ∈ E 0 , e l s e ≤ 0 A_{ij}= \begin{cases} 1, & (v_i,v_j)\in E\\ 0, & else \le 0 \end{cases} Aij={1,0,(vi,vj)∈Eelse≤0
图 G
的邻接矩阵表示如下所示:
在 Python
中,可以将其实现为一个列表的列表:
adj = [[0,1,1,0,0,0,0],[1,0,0,1,1,0,0],[1,0,0,0,0,1,1],[0,1,0,0,0,0,0],[0,1,0,0,0,0,0],[0,0,1,0,0,0,0],[0,0,1,0,0,0,0]]
邻接矩阵是一种直观的表示方法,可以方便的将其可视化为二维数组。使用邻接矩阵的一个主要优点是,可以在恒定时间内检查两个节点是否相连,因此是检测图中是否存在边的有效表示方法。此外,它还可用于执行矩阵运算,这对某些图算法非常有用,例如计算两个节点之间的最短路径。
但邻接矩阵表示法添加或删除节点的成本很高,因为需要调整矩阵的大小。使用邻接矩阵的主要缺点之一是空间复杂性:随着图中节点数量的增加,存储邻接矩阵所需的空间也呈指数级增长,邻接矩阵的空间复杂度为 O ∣ V ∣ 2 O|V|^2 O∣V∣2,其中 ∣ V ∣ |V| ∣V∣ 表示图中的节点数。
总的来说,虽然邻接矩阵是表示小型图的有用数据结构,但由于其空间复杂度,对于大型图来说可能并不实用。此外,添加或删除节点的开销使其在动态变化的图中效率较低。
另一种常用的图存储方式是边列表 (edge list
)。边列表是一个图中所有边的列表。每条边由一个元组或一对节点表示,边列表还可以包括每条边的权重,边列表的空间复杂度为 O ∣ E ∣ O|E| O∣E∣ ,其中 ∣ E ∣ |E| ∣E∣ 是边的数量。这种表示方法是 networkx
创建图时使用的结构:
edge_list = [(0, 1), (0, 2), (1, 3), (1, 4), (2, 5), (2, 6)]
边列表对于存储稀疏图更有效,因为稀疏图中边的数量远远少于节点的数量。但是,在边列表中检查两个节点是否相连需要遍历整个列表,这对于有很多边的大型图来说可能很耗时。因此,边列表更常用于对空间有要求的应用中。
第三种常用的表示方法是邻接表 (adjacency list
)。它由一组成对的列表组成,其中每一对代表图中的一个节点及其相邻节点。根据实现方式的不同,这些列表对可以存储在链表、字典或其他数据结构中。例如,可以使用字典表示图的邻接表:
adj_list = {0: [1, 2],1: [0, 3, 4],2: [0, 5, 6],3: [1],4: [1],5: [2],6: [2]
}
与邻接矩阵或边列表相比,邻接表有如下优点。首先,其空间复杂度为 O ∣ V ∣ + O ∣ E ∣ O|V| + O|E| O∣V∣+O∣E∣ ,其中 ∣ V ∣ |V| ∣V∣ 是节点数, ∣ E ∣ |E| ∣E∣ 是边数,这比稀疏图中邻接矩阵的空间复杂度 O ∣ V ∣ 2 O|V|^2 O∣V∣2 更低。其次,它可以用于高效迭代节点的相邻顶点,这对于许多图算法而言都非常有用。最后,添加节点或边可以在恒定时间内完成。
但,检查两个节点是否相连可能比使用邻接矩阵慢。这是因为它需要遍历其中一个顶点的邻接列表,而这对于大型图来说非常耗时。每种数据结构都有优缺点,选择何种数据结构通常取决于具体的应用和要求。
3. 图算法
图算法对于解决与图相关的问题至关重要,例如查找两个节点之间的最短路径或查找图中的关键节点。本节将讨论两种图遍历算法:广度优先搜索 (Breadth-first search
, BFS
) 和深度优先搜索 (Depth-first search
, DFS
)。
3.1 广度优先搜索
广度优先搜索 (Breadth-first search
, BFS
) 是一种图遍历算法,从根节点开始,在移动到下一层节点之前,先探索特定层的所有相邻节点。其算法思想是,从图中某一节点 v i v_i vi 开始,从 v i v_i vi 出发,依次访问 v i v_i vi 的所有未被访问过的邻居 u 1 , u 2 , … , u n u_1,u_2,…,u_n u1,u2,…,un;然后再顺序访问 u 1 , u 2 , … , u n u1,u_2,…,u_n u1,u2,…,un 的所有未被访问过的邻居,如此一层一层执行,直到图中所有的节点都被访问到为止。具体实现时,可以维护一个要访问的节点队列并在将每个访问过的节点添加到队列时标记该节点,然后,算法从队列中移除下一个节点并探索其所有邻居,如果它们尚未被访问过,则将它们添加到队列中,
BFS
的算法过程如下所示:
接下来,使用 Python
实现 BFS
。
(1) 创建图 G
,使用 add_edges_from()
方法添加边:
G = nx.Graph()
G.add_edges_from([('A', 'B'), ('A', 'C'), ('B', 'D'), ('B', 'E'), ('C', 'F'), ('C', 'G')])
(2) 定义函数 bfs()
,在图上实现 BFS
算法,函数需要两个参数:图对象和搜索的起始节点:
def bfs(graph, node):
(3) 初始化两个列表 (visited
和 queue
) 并添加起始节点。visited
记录搜索过程中已访问过的节点,而 queue
则存储需要访问的节点:
visited, queue = [node], [node]
(4) 使用 while
循环,直到 queue
为空。在循环中,使用 pop(0)
方法移除 queue
中的第一个节点,并将结果存储在变量 node
中:
while queue:node = queue.pop(0)
(5) 使用 for
循环遍历节点的邻居。对于每个尚未被访问的邻居,使用 append()
方法将其添加到 visited
和 queue
的末尾。完成后,返回 visited
列表:
for neighbor in graph[node]:if neighbor not in visited:visited.append(neighbor)queue.append(neighbor)return visited
(6) 使用参数 G
和起始节点 A
调用 bfs()
函数:
bfs(G, 'A')
(7) 函数将按照节点被访问的顺序返回 visited
列表:
['A', 'B', 'C', 'D', 'E', 'F', 'G']
BFS
适用于查找非权重图中两个节点之间的最短路径。这是因为该算法按照节点与起始节点的距离依次访问节点,因此第一次访问目标节点时,它一定是沿着与起始节点距离最短的路径。
除了寻找最短路径外,BFS
还可用于检查图是否连通或寻找图的所有连通部分,以及网络爬虫、社交网络分析和网络中的最短路径路由等应用。
BFS
的时间复杂度为 O ∣ V ∣ + O ∣ E ∣ O|V| + O|E| O∣V∣+O∣E∣ ,其中 ∣ V ∣ |V| ∣V∣ 是图中的节点数, ∣ E ∣ |E| ∣E∣ 是图中的边数。对于连通度较高的图或稀疏图来说,计算代价较高。为了缓解这一问题,提出几种 BFS
变体,如双向 BFS
和 A* 搜索
,使用启发式方法减少需要探索的节点数量。
3.2 深度优先搜索
深度优先搜索 (Depth-first search
, BFS
) 是一种递归算法,它从根节点开始,在回溯之前尽可能沿着每个分支进行探索。
它选择一个节点,探索其所有未探索的邻居,访问第一个未探索的邻居,只有当所有邻居都被访问过之后才会回溯。这样,在回溯探索其他分支之前,它会从起始节点沿着尽可能深的路径探索图,直到所有节点都被探索完为止。
DFS
的算法过程如下:
接下来,使用 Python
实现 DFS
。
(1) 首先初始化一个空列表 visited
:
visited = []
(2) 定义函数 dfs()
,将 visited
、graph
和 node
作为参数:
def dfs(visited, graph, node):
(3) 如果当前节点不在 visited
列表中,就将其添加到列表中:
if node not in visited:visited.append(node)
(4) 然后,遍历当前节点的每个邻居。对于每个邻居节点,都会递归调用 dfs()
函数,并将 visited
、graph
和邻居节点作为参数:
for neighbor in graph[node]:visited = dfs(visited, graph, neighbor)
(5) dfs()
函数继续以深度优先方式探索图,访问每个节点的所有邻居,直到没有未访问的邻居为止。最后,返回列表 visited
:
return visited
(6) 调用 dfs()
函数时,visited
设置为空列表,G
为待遍历图,A
为起始节点:
print(dfs(visited, G, 'A'))
(7) 函数将按照节点被访问的顺序返回列表 visited
:
['A', 'B', 'D', 'E', 'C', 'F', 'G']
DFS
在解决多种图问题时都非常有用,例如查找图中连接部分、拓扑排序和解决迷宫问题,尤其适用于查找图中的循环,因为它以深度优先的顺序遍历图,当且仅当一个节点在遍历过程中被访问两次时,循环才存在。
与 BFS
类似,其时间复杂度为 O ∣ V ∣ + O ∣ E ∣ O|V| + O|E| O∣V∣+O∣E∣ ,其中 ∣ V ∣ |V| ∣V∣ 是图中的节点数, ∣ E ∣ |E| ∣E∣ 是图中的边数。它需要的内存更少,但不能保证得到最浅路径解 (shallowest path solution
)。与 BFS
不同,使用 DFS
可能会陷入死循环。
图论中的许多其他算法都建立在 BFS
和 DFS
的基础上,例如 Dijkstra 最短路径算法
、Kruskal 最小生成树算法
等。
小结
在本节中,我们介绍了图论的基本内容,图论是研究图和网络的数学分支。首先定义了什么是图,并解释了有向图、加权图和连接图等不同类型的图。然后,介绍了基本图对象和度量指标。此外,还讨论了图的邻接矩阵及其他表示方法。最后,探讨了两种基本图算法,即广度优先算法和深度优先算法,它们是设计更复杂图算法的基础。
系列链接
图神经网络实战——图神经网络(Graph Neural Networks, GNN)基础
相关文章:

图神经网络实战——图论
图神经网络实战——图论 0. 前言1. 图属性1.1 有向图和无向图1.2 加权图与非加权图1.3 连通图非连通图1.4 其它图类型 2. 图概念2.1 基本对象2.2 图的度量指标2.2 邻接矩阵表示法 3. 图算法3.1 广度优先搜索3.2 深度优先搜索 小结系列链接 0. 前言 图论 (Graph theory) 是数学…...
【PHP进阶】Rabbitmq的实际使用
RabbitMQ是一个流行的消息队列中间件,它提供了可靠的消息传递机制。在使用RabbitMQ时,有几个重要的概念需要了解: 消息队列(Message Queue):RabbitMQ中的核心概念之一。它是消息的缓冲区,用于存…...

如何解决机器视觉高速图像处理软件的加密需求?
高速图像处理在机器视觉中的应用重要性 在机器视觉行业中,高速图像处理软件的作用至关重要,它使得机器能够迅速分析和处理成千上万的图像数据。这种能力在制造业、安防系统、交通监控等多个领域发挥着核心作用,如在制造业中,高速…...

Linux的条件变量
条件变量 条件变量本身不是锁,但是它可以造成线程阻塞。通常于互斥锁配合使用。给多线程提供一个会和的场合。 使用互斥量保护共享数据使用条件变量可以造成线程阻塞,等待某个条件的发生,当条件满足的时候解除阻塞。 条件变量的两个动作&a…...

【Python笔记-设计模式】状态模式
一、说明 状态模式是一种行为设计模式,用于解决对象在不同状态下具有不同行为 (一) 解决问题 在对象行为根据对象状态而改变时,规避使用大量的条件语句来判断对象的状态,提高系统可维护性 (二) 使用场景 当对象的行为取决于其状态&#…...
Pytorch 复习总结 5
Pytorch 复习总结,仅供笔者使用,参考教材: 《动手学深度学习》Stanford University: Practical Machine Learning 本文主要内容为:Pytorch 卷积神经网络。 本文先介绍了 Pytorch 语法汇总: Pytorch 张量的常见运算、…...

Codeforces Round 930 (Div. 2)
Codeforces Round 930 (Div. 2) Codeforces Round 930 (Div. 2) A. Shuffle Party 题意: 给出长度为n的整数数组a, a i a_i ai i,对于k>2的下标进行运算,设d为k除本身外最大的除数, 操作为交换( a k a_k ak…...
c语言求平方与倒数序列的部分和
本题要求对两个正整数m和n(m≤n)编写程序,计算序列和m21/m(m1)21/(m1)⋯n21/n。 输入格式: 输入在一行中给出两个正整数m和n(m≤n),其间以空格分开。 输出格式: 在一行中按照“sum S”的格式输出部分和…...

Vue-4
自定义创建项目 目标:基于 VueCli 自定义创建项目架子 大致步骤: 安装脚手架创建项目 vue create 项目名称选择自定义 选择 Manually select features 这一项 step-1:按下空格 : 选择/取消--勾选请选择:Babel、Router、CSS、Linterstep-2…...

【Acwing】差分矩阵
图1:a和b数组映射表 由于a是b的前缀和数组,因此改变b[ x1][ y1]之后,受到影响的a中元素如右半图所示 图2:求b数组的前缀和 #include<bits/stdc.h> using namespace std;int n,m,q; int a[1010][1010]; int b[1010][1010]…...

Linux系统加固:如何有效管理系统账号
Linux系统加固:如何有效管理系统账号 1.1 口令重复次数限制1.2 避免系统存在uid相同的账号1.3 空密码的帐户1.4 口令复杂度1.5 口令生存期1.6 登录失败次数锁定策略 💖The Begin💖点点关注,收藏不迷路💖 在Linux系统中…...

在Windows中安装PyTorch
文章目录 1. 创建虚拟环境2. 检查显卡版本和CUDA3. 下载链接4. 下载5. 等待6. 检测 1. 创建虚拟环境 具体查看我之前写的 《在Windows中利用Python的venv和virtualenv创建虚拟环境》 2. 检查显卡版本和CUDA 这种情况是需要电脑上有单独的英伟达的显卡、或者英伟达的显卡和集显…...

助力智能化农田作物除草,基于YOLOv7【tiny/l/x】不同系列参数模型开发构建农田作物场景下玉米苗、杂草检测识别分析系统
在我们前面的系列博文中,关于田间作物场景下的作物、杂草检测已经有过相关的开发实践了,结合智能化的设备可以实现只能除草等操作,玉米作物场景下的杂草检测我们则少有涉及,这里本文的主要目的就是想要基于YOLOv7系列的模型来开发…...

linux nasm汇编中调用printf不报错,但调用scanf报错。抛出了分段错误(核心转储)
当我写了如下汇编时 ; nasm -f elf64 -g -F dwarf charsin.asm ; gcc charsin.o -no-pie -o charsin ; ld -o eatclib eatclib.o ; gdb eatclib[SECTION .data]SPrompt db Enter string data, followed by Enter: ,0IPrompt db Enter an integer value, followed by Enter: ,1…...

Linux系统——Nginx负载均衡模式
目录 一、Nginx优点 二、Nginx配置项——Conf Upstream 模块 三、Nginx负载均衡 1.负载均衡策略 1.1轮询 1.2IP_hash 1.3URL_hash 1.4Least_conn 1.5Weight 1.6Fair 2.Nginx负载均衡配置状态参数 3.什么是会话保持 3.1会话保持有什么作用呢 3.2Nginx会话保持 3…...
【自然语言处理之语言模型】讲解
自然语言处理之语言模型 1. 前言2. 传统语言模型3. 神经语言模型4. 训练语言模型5. 评估语言模型6. 总结 1. 前言 自然语言处理(Natural Language Processing,NLP)是计算机科学、人工智能和语言学交叉的一个领域,它研究计算机和人…...

输入一个整数n,输出这个整数的二进制的0和1的个数
输入一个整数n,输出这个整数的二进制的0和1的个数:除二取余法 代码: #include <cstdio> int main() {int n;scanf_s("%d", &n);int arr[2] { 0 };while (n) {int yu n % 2;arr[yu];n n / 2;}printf("0的个数是:…...

初阶数据结构:链表相关题目练习(补充)
目录 1. 单链表相关练习题1.1 移除链表元素1.2 反转链表1.3 链表的中间结点1.4 链表的倒数第k个结点1.5 合并两个有序链表1.6 链表分割1.7 链表的回文结构1.8 相交链表1.9 判断一个链表中是否有环1.10 寻找环状链表相遇点1.11 链表的深度拷贝 1. 单链表相关练习题 注࿱…...

java: 错误: 不支持发行版本 5
目录 一、问题描述 二、解决办法 方法一:修改idea设置中的jdk版本 方法二:配置pom.xml文件 方法三:配置maven的xml文件(推荐) 三、结果 一、问题描述 问题描述:今天创建了一个maven项目,…...
springSecruity--->和springboot结合的跨域问题
🤦♂️这个是我在springboot中使用springSecruity写一个小demo时遇到的问题,记录下来🤦♂️ 文章目录 跨域请求springboot项目中使用springSecruity导致跨域请求CrossOrigin请求失效解决方法springboot 中的跨域方法 跨域请求 什么是跨…...

Chapter03-Authentication vulnerabilities
文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...

大数据学习栈记——Neo4j的安装与使用
本文介绍图数据库Neofj的安装与使用,操作系统:Ubuntu24.04,Neofj版本:2025.04.0。 Apt安装 Neofj可以进行官网安装:Neo4j Deployment Center - Graph Database & Analytics 我这里安装是添加软件源的方法 最新版…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
Objective-C常用命名规范总结
【OC】常用命名规范总结 文章目录 【OC】常用命名规范总结1.类名(Class Name)2.协议名(Protocol Name)3.方法名(Method Name)4.属性名(Property Name)5.局部变量/实例变量(Local / Instance Variables&…...

Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...

Golang——6、指针和结构体
指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

【网络安全】开源系统getshell漏洞挖掘
审计过程: 在入口文件admin/index.php中: 用户可以通过m,c,a等参数控制加载的文件和方法,在app/system/entrance.php中存在重点代码: 当M_TYPE system并且M_MODULE include时,会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...