当前位置: 首页 > news >正文

从原图到线图再到反推:网络图几何与拓扑的结合分析

从原图到线图再到反推:网络图几何与拓扑的结合分析

在网络科学与图论应用中,原始图 GGG 与其 线图 L(G)L(G)L(G) 的关系是一个非常经典的课题。通过线图,我们能够将“边”提升为“节点”,进而从另一种视角来观察图的结构与性质。在本文中,我们不仅展示如何从原图构造线图,还会结合几何计算与优化方法,探讨如何利用线图和几何约束反推出原图的空间嵌入。


1. 原图 G 的构建与边长计算

首先,我们创建一个带有坐标的无向图 G=(V,E)G=(V,E)G=(V,E)。节点 VVV 的位置以字典 pos 给定,边 EEE 则通过 edges 列表指定。

对于任意一条边 (u,v)(u,v)(u,v),其长度由欧氏距离给出:
ℓ(u,v)=(xu−xv)2+(yu−yv)2 \ell(u,v) = \sqrt{ (x_u - x_v)^2 + (y_u - y_v)^2 } (u,v)=(xuxv)2+(yuyv)2
其中 (xu,yu)(x_u, y_u)(xu,yu)(xv,yv)(x_v, y_v)(xv,yv) 分别为节点 (u,v) 的坐标。

import networkx as nx
import numpy as np
from scipy.spatial import distance
from scipy.optimize import least_squares
import matplotlib.pyplot as plt
import seaborn as snssns.set_theme(style="whitegrid", font="SimHei", rc={"axes.unicode_minus": False})# 创建图
G = nx.Graph()
pos = {0: (0, 0), 1: (4, 0), 2: (6, 0), 3: (0, 2), 4: (0, 3),5: (-4, 0), 6: (0, -1), 7: (0, -2), 8: (0, -4), 9: (0, -6),10: (4, 2), 11: (4, 4), 12: (5, 2), 13: (5, 3), 14: (2, 4),15: (6, 4), 16: (-2, 2), 17: (-2, 5), 18: (-2, 6), 19: (2, 6),20: (-3, 5), 21: (2, -2), 22: (4, -2), 23: (6, -2), 24: (4, -4),25: (4, -5), 26: (2, -4), 27: (5, -4), 28: (-1, -1), 29: (-1, -2),30: (-2, -2), 31: (-2, -4), 32: (-3, -4),
}
edges = [(0, 1), (1, 2), (0, 3), (3, 4), (0, 5), (0, 6), (6, 7), (7, 8), (8, 9), (1, 10), (10, 11), (10, 12), (12, 13),(11, 14), (11, 15), (4, 14), (14, 19), (3, 16), (16, 17), (17, 18), (18, 19), (17, 20), (7, 21), (21, 22), (22, 23),(1, 22), (22, 24), (24, 25), (8, 26), (24, 26), (24, 27),(21, 26), (6, 28), (28, 29), (7, 29), (29, 30), (30, 31),(8, 31), (31, 32),
]
G.add_edges_from(edges)# 将节点坐标转换为 NumPy 数组
node_index = {n: i for i, n in enumerate(G.nodes())}
pos_array = np.array([pos[n] for n in G.nodes()])# 获取所有边的起点和终点索引
edges_array = np.array([[node_index[u], node_index[v]] for u, v in G.edges()])
# 取出对应坐标
coords_u = pos_array[edges_array[:, 0]]
coords_v = pos_array[edges_array[:, 1]]
lengths = np.linalg.norm(coords_u - coords_v, axis=1)for (u, v), l in zip(G.edges(), lengths):G.edges[u, v]['length'] = l# 创建图形和坐标轴
fig, ax = plt.subplots(figsize=(8, 8))
# 绘制边
nx.draw_networkx_edges(G, pos, ax=ax)
# 绘制节点
nx.draw_networkx_nodes(G, pos, node_size=150, node_color='skyblue', ax=ax)
# 绘制标签
nx.draw_networkx_labels(G, pos, font_size=10, font_color='black', ax=ax)# 在边上显示边长
edge_labels = {}
for u, v, d in G.edges(data=True):val = d['length']if val == int(val):  # 如果是整数edge_labels[(u, v)] = f"{int(val)}"else:# 保留两位小数edge_labels[(u, v)] = f"{val:.2f}"nx.draw_networkx_edge_labels(G, pos,edge_labels=edge_labels,font_size=10,font_color='red'
)# 设置坐标轴范围
x_vals, y_vals = zip(*pos.values())
ax.set_xlim(min(x_vals)-1, max(x_vals)+1)
ax.set_ylim(min(y_vals)-1, max(y_vals)+1)# 设置刻度与网格
ax.set_xticks(range(int(min(x_vals))-1, int(max(x_vals))+2))
ax.set_yticks(range(int(min(y_vals))-1, int(max(y_vals))+2))
ax.grid(True, linestyle='--', color='gray', alpha=0.5)ax.set_aspect('equal')  # 坐标轴长度相等
plt.title('原图 G')
plt.show()

在这里插入图片描述

代码中通过 np.linalg.norm(coords_u - coords_v, axis=1) 批量计算所有边的长度,并存入边属性。最后在可视化时,将边长以标签标注在图上。


2. 线图 L 的构建与几何表示

给定原图 G=(V,E)G=(V,E)G=(V,E),线图定义如下:

节点集
V(L)=E(G) V(L) = E(G) V(L)=E(G)
即原图的每一条边都成为线图中的一个节点。

边集
E(L)=((u,v),(v,w))∣(u,v),(v,w)∈E(G) E(L) = { ((u,v),(v,w)) \mid (u,v),(v,w) \in E(G) } E(L)=((u,v),(v,w))(u,v),(v,w)E(G)
即若原图两条边共享一个端点,它们对应的线图节点相连。

在可视化中,我们将线图的节点坐标定义为原图边的中点:
muv=(xu+xv2,yu+yv2) m_{uv} = \left( \frac{x_u+x_v}{2}, \frac{y_u+y_v}{2} \right) muv=(2xu+xv,2yu+yv)
同时,将原图的边长作为线图节点的属性,并用它来设置节点大小。这样,线图 (L) 不仅保留了原图的拓扑关系,还反映了几何信息。

# 创建线图
L = nx.line_graph(G)# 给线图节点定义位置:取边两端节点的中点
line_pos = {}
for edge in L.nodes():u, v = edgex = (pos[u][0] + pos[v][0]) / 2y = (pos[u][1] + pos[v][1]) / 2line_pos[edge] = (x, y)# 将原图边的长度存入线图节点属性,并用来设置节点大小
node_sizes = []
for edge in L.nodes():length = G.edges[edge]['length']  # 获取原图边长度L.nodes[edge]['length'] = length  # 存入节点属性node_sizes.append(length * 300)   # 放大倍数可调,便于可视化# 绘制线图
fig, ax = plt.subplots(figsize=(10, 10))# 绘制边
nx.draw_networkx_edges(L, pos=line_pos, ax=ax)
# 绘制节点,大小根据原图边长
nx.draw_networkx_nodes(L, pos=line_pos, node_size=node_sizes, node_color="lightgreen", ax=ax)
# 绘制标签
nx.draw_networkx_labels(L, pos=line_pos, font_size=8, ax=ax)# 设置坐标轴范围和刻度,让网格对齐
x_vals, y_vals = zip(*line_pos.values())
ax.set_xlim(min(x_vals)-1, max(x_vals)+1)
ax.set_ylim(min(y_vals)-1, max(y_vals)+1)
ax.set_xticks([round(v) for v in range(int(min(x_vals))-1, int(max(x_vals))+2)])
ax.set_yticks([round(v) for v in range(int(min(y_vals))-1, int(max(y_vals))+2)])# 添加网格
ax.grid(True, linestyle='--', color='gray', alpha=0.5)
ax.set_aspect('equal')
ax.set_title("线图 L - 节点大小表示原图边长度")
plt.show()

在这里插入图片描述


3. 原图 G 与线图 L 的叠加

为了直观对比,我们将原图 GGG 与线图 LLL 绘制在同一张图上:

  • 蓝色节点与灰色边表示原图 GGG
  • 绿色节点与粉色边表示线图 LLL

叠加图能够清晰展示:原图的边是如何“转化”为线图的节点,而两条共享端点的边又如何在 LLL 中相连。

fig, ax = plt.subplots(figsize=(12, 12))# 绘制原图
nx.draw_networkx_edges(G, pos, ax=ax, edge_color='gray', alpha=1, width=2, label='G边')
nx.draw_networkx_nodes(G, pos, ax=ax, node_size=300, node_color='skyblue', label='G点')
nx.draw_networkx_labels(G, pos, ax=ax, font_size=10, font_color='black')# 绘制线图
nx.draw_networkx_edges(L, line_pos, ax=ax, edge_color='salmon', alpha=0.5, width=2, label='L边')
nx.draw_networkx_nodes(L, line_pos, ax=ax, node_size=200, node_color='lightgreen', label='L点')
nx.draw_networkx_labels(L, line_pos, ax=ax, font_size=8, font_color='darkgreen')# 设置坐标轴和网格
x_vals, y_vals = zip(*list(pos.values()) + list(line_pos.values()))
ax.set_xlim(min(x_vals)-0.5, max(x_vals)+0.5)
ax.set_ylim(min(y_vals)-0.5, max(y_vals)+0.5)# 设置刻度与网格
ax.set_xticks(range(int(min(x_vals))-1, int(max(x_vals))+2))
ax.set_yticks(range(int(min(y_vals))-1, int(max(y_vals))+2))
ax.grid(True, linestyle='--', color='gray', alpha=0.5)ax.set_aspect('equal')
ax.set_title("原始网络图G及其线图L叠加示意图", fontsize=16)# --- 添加图例
handles, labels = ax.get_legend_handles_labels()
by_label = dict(zip(labels, handles))
ax.legend(by_label.values(), by_label.keys(), loc='upper right', fontsize=10)plt.show()

在这里插入图片描述


4. 从 L 反推 G:几何约束与优化

接下来,我们尝试从线图的几何信息反推出原图节点的位置。

4.1 约束条件

假设原图中有一条边 (u,v)(u,v)(u,v),其在线图中对应的节点为 muvm_{uv}muv,并具有以下约束:

中点约束
xu+xv2=mx,yu+yv2=my \frac{x_u+x_v}{2} = m_x, \quad \frac{y_u+y_v}{2} = m_y 2xu+xv=mx,2yu+yv=my
其中 (mx,my)(m_x, m_y)(mx,my) 是线图节点的坐标。

边长约束
(xu−xv)2+(yu−yv)2=ℓ(u,v) \sqrt{(x_u-x_v)^2 + (y_u-y_v)^2} = \ell(u,v) (xuxv)2+(yuyv)2=(u,v)
这两个约束条件共同确保了恢复的坐标既符合几何关系,也符合边长。

4.2 最小二乘优化

我们将所有约束写成残差函数,通过 最小二乘法进行求解:

min⁡x∑(u,v)∈E(G)[(xu+xv2−mx)2−(yu+yv2−my)2−(∣(xu,yu)−(xv,yv)∣−ℓ(u,v))2] \min_{x} \sum_{(u,v) \in E(G)}\Bigg[\Big(\tfrac{x_u+x_v}{2}-m_x\Big)^2 - \Big(\tfrac{y_u+y_v}{2}-m_y\Big)^2 - \Big(| (x_u,y_u)-(x_v,y_v)| - \ell(u,v)\Big)^2\Bigg] xmin(u,v)E(G)[(2xu+xvmx)2(2yu+yvmy)2((xu,yu)(xv,yv)(u,v))2]

使用 scipy.optimize.least_squares,我们可以数值化地恢复出原图节点的近似坐标。

# 反推原图 G 节点坐标
nodes = list({n for e in line_pos.keys() for n in e})
node_idx = {n:i for i,n in enumerate(nodes)}# 初始值:使用原图 pos 并加少量扰动
x0 = np.zeros(len(nodes)*2)
for i, n in enumerate(nodes):x0[2*i] = pos[n][0]x0[2*i+1] = pos[n][1]def fun(x):res = []for u,v in line_pos.keys():xu, yu = x[2*node_idx[u]], x[2*node_idx[u]+1]xv, yv = x[2*node_idx[v]], x[2*node_idx[v]+1]mx, my = line_pos[(u,v)]# 中点约束res.append((xu+xv)/2 - mx)res.append((yu+yv)/2 - my)# 边长约束,使用已有边长度属性l = G.edges[u,v]['length']res.append(np.sqrt((xu-xv)**2 + (yu-yv)**2) - l)return res# 最小二乘求解
result = least_squares(fun, x0)
coords = result.x.reshape(-1,2)
restored_pos = {n:(coords[i,0], coords[i,1]) for i,n in enumerate(nodes)}# 构造恢复的 G 图
G_restored = nx.Graph()
G_restored.add_nodes_from(nodes)
G_restored.add_edges_from(line_pos.keys())# 可视化恢复图
plt.figure(figsize=(10, 10))nx.draw_networkx_nodes(G_restored, restored_pos, node_size=300, node_color='skyblue')
nx.draw_networkx_edges(G_restored, restored_pos, width=2, edge_color='gray')
nx.draw_networkx_labels(G_restored, restored_pos, font_size=10, font_color='black')# 设置坐标轴和网格
x_vals, y_vals = zip(*restored_pos.values())
plt.xlim(min(x_vals)-1, max(x_vals)+1)
plt.ylim(min(y_vals)-1, max(y_vals)+1)
plt.xticks(range(int(min(x_vals))-1, int(max(x_vals))+2))
plt.yticks(range(int(min(y_vals))-1, int(max(y_vals))+2))
plt.grid(True, linestyle='--', color='gray', alpha=0.5)
plt.gca().set_aspect('equal')
plt.title("使用L图恢复为G图")
plt.show()

在这里插入图片描述


5. 关键前提与局限性

需要特别强调的是,几何信息本身不足以恢复原图

  • 如果线图仅仅保留了“中点坐标”和“边长”,而没有保留 拓扑对应关系(即线图的节点是 (u,v)(u,v)(u,v),告诉我们它对应的是原图的哪条边),那么恢复问题会变得 不可解或多解
  • 因此,反推原图的前提是:线图 LLL 必须反映原图 GGG 的边的真实连接关系。也就是说,LLL 中的节点必须明确指出它来源于哪条原图边。

只有在这个前提下,几何约束和拓扑约束结合,才能保证原图的恢复是合理的。

lt.show()


[外链图片转存中...(img-c9PmAeMW-1759760205842)]------## 关键前提与局限性需要特别强调的是,**几何信息本身不足以恢复原图**。- 如果线图仅仅保留了“中点坐标”和“边长”,而没有保留 **拓扑对应关系**(即线图的节点是 $(u,v)$,告诉我们它对应的是原图的哪条边),那么恢复问题会变得 **不可解或多解**。
- 因此,反推原图的前提是:**线图 $L$ 必须反映原图 $G$ 的边的真实连接关系**。也就是说,$L$ 中的节点必须明确指出它来源于哪条原图边。只有在这个前提下,几何约束和拓扑约束结合,才能保证原图的恢复是合理的。
http://www.dtcms.com/a/450017.html

相关文章:

  • Lua下载和安装教程(附安装包)
  • JAVA实验课程第五次作业分析与代码示例
  • 龙口网站制作公司深圳知名设计公司有哪些
  • 网站数据修改网页界面设计的起源
  • 东莞建设网站官网住房和城乡wordpress 如何修改like和dislike
  • Gopher二次编码原因解析
  • 【ARM汇编语言基础】-数据处理指令(七)
  • 汇编与反汇编
  • 福州建设网站shopee怎么注册开店
  • 建立网站站点的目的贵州二级站seo整站优化排名
  • 阳江做网站多少钱企业网站推广方法有哪些
  • sm2025 模拟赛11 (2025.10.5)
  • python镜像源配置
  • 4.寻找两个正序数组的中位数-二分查找
  • 理解CC++异步IO编程:Epoll入门
  • wordpress房屋网站模板微信小程序
  • 阿里网站建设视频教程WordPress云媒体库
  • SpringCloud 入门 - Nacos 配置中心
  • Windows 下使用 Claude Code CLI 启动 Kimi
  • 网站推广的基本方式抖音特效开放平台官网
  • 湖南网站排名wordpress插件seo
  • WindowsKyLin:nginx安装与配置
  • 【剑斩OFFER】算法的暴力美学——最大连续1的个数 III
  • UNIX下C语言编程与实践32-UNIX 僵死进程:成因、危害与检测方法
  • 论坛开源网站源码首页优化排名
  • 网站建设策请seo的人帮做网站排名
  • 旅游网站后台html模板做网站的做app的
  • 网站备案回访问题效果好的网站制作
  • Unity 光源
  • 应急响应