import sys
from collections import deque
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # 解决中文显示问题
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
class DinicVisual:
def __init__(self, n, edges, visualize=True):
"""
:param n: 节点数
:param edges: 边列表 [(u, v, lb, ub)]
:param visualize: 是否可视化
"""
self.n = n
self.original_edges = edges
self.visualize = visualize
self.fig, self.ax = plt.subplots(figsize=(14, 10))
self.fig.suptitle("无源汇上下界可行流算法动态可视化", fontsize=16)
# 初始化超级源汇
self.s = n
self.t = n + 1
self.total_nodes = n + 2
# 计算每个节点的流量差
self.A = [0] * n
for u, v, lb, ub in edges:
self.A[u] -= lb
self.A[v] += lb
# 创建Dinic数据结构
self.graph = [[] for _ in range(self.total_nodes)]
self.level = [-1] * self.total_nodes
self.cur = [0] * self.total_nodes
self.edge_info = {} # 存储边信息
# 添加原图中的边
self.original_edge_refs = []
for i, (u, v, lb, ub) in enumerate(edges):
cap = ub - lb
# 添加边并记录信息
self.add_edge(u, v, cap, (i, lb, ub, f"e{i}"))
self.original_edge_refs.append((u, v, len(self.graph[u]) - 1, lb))
# 添加超级源汇的边
self.total_flow = 0
for i in range(n):
if self.A[i] > 0:
self.add_edge(self.s, i, self.A[i], (f"s→{i}", "source"))
self.total_flow += self.A[i]
elif self.A[i] < 0:
self.add_edge(i, self.t, -self.A[i], (f"{i}→t", "sink"))
# 初始化可视化
if self.visualize:
self.initialize_visualization()
def add_edge(self, u, v, cap, info=None):
"""添加边并存储信息"""
forward = [v, cap, 0, info] # [目标, 容量, 流量, 信息]
reverse = [u, 0, 0, None] # 反向边
forward[2] = reverse
reverse[2] = forward
self.graph[u].append(forward)
self.graph[v].append(reverse)
# 存储边信息用于可视化
if info:
self.edge_info[(u, v)] = {
'capacity': cap,
'flow': 0,
'info': info
}
return forward
def bfs(self):
"""BFS分层并可视化"""
self.level = [-1] * self.total_nodes
queue = deque([self.s])
self.level[self.s] = 0
# 可视化:显示BFS搜索过程
if self.visualize:
self.visualize_step(f"BFS分层: 访问源点s (L0)")
plt.pause(1.0)
while queue:
u = queue.popleft()
for i, edge in enumerate(self.graph[u]):
v, cap, rev, info = edge
if cap > 0 and self.level[v] == -1:
self.level[v] = self.level[u] + 1
queue.append(v)
# 可视化:显示新访问的节点
if self.visualize:
if v < self.n:
node_label = f"节点{v}"
else:
node_label = "汇点t" if v == self.t else "源点s"
self.visualize_step(f"BFS分层: 访问{node_label} (L{self.level[v]})")
plt.pause(0.3)
return self.level[self.t] != -1
def dfs(self, u, t, flow, path=None):
"""DFS查找增广路径并可视化"""
if path is None:
path = []
if u == t:
# 可视化:显示找到的增广路径
if self.visualize:
path_desc = "→".join([f"{'s' if p == self.s else 't' if p == self.t else p}" for p in path + [t]])
self.visualize_step(f"找到增广路径: {path_desc}\n流量: {flow}")
plt.pause(1.5)
return flow
for i in range(self.cur[u], len(self.graph[u])):
self.cur[u] = i
edge = self.graph[u][i]
v, cap, rev, info = edge
if cap > 0 and self.level[v] == self.level[u] + 1:
# 可视化:显示当前探索的边
if self.visualize:
edge_desc = self.get_edge_description(u, v)
self.visualize_step(f"探索: {edge_desc} (剩余容量: {cap})")
plt.pause(0.5)
f = self.dfs(v, t, min(flow, cap), path + [u])
if f > 0:
# 更新边流量
edge[1] -= f
rev[1] += f
# 更新可视化信息
if (u, v) in self.edge_info:
self.edge_info[(u, v)]['flow'] += f
elif (v, u) in self.edge_info: # 处理反向边
self.edge_info[(v, u)]['flow'] -= f
# 可视化:显示流量更新
if self.visualize:
edge_desc = self.get_edge_description(u, v)
self.visualize_step(f"更新: {edge_desc}\n增加流量: {f}")
plt.pause(0.8)
return f
return 0
def max_flow(self):
"""计算最大流并动态可视化"""
total_flow = 0
iteration = 1
while self.bfs():
self.cur = [0] * self.total_nodes
if self.visualize:
self.visualize_step(f"开始阶段 {iteration} (分层完成)")
plt.pause(1.0)
while True:
flow = self.dfs(self.s, self.t, float('inf'))
if flow == 0:
break
total_flow += flow
if self.visualize:
self.visualize_step(f"阶段 {iteration} 完成\n累计流量: {total_flow}/{self.total_flow}")
plt.pause(1.0)
iteration += 1
# 检查可行解
if total_flow != self.total_flow:
if self.visualize:
self.visualize_step(f"无可行解!\n需求流量: {self.total_flow}, 实际流量: {total_flow}")
plt.pause(3.0)
return None
# 计算原图中每条边的实际流量
flows = []
for u, v, idx, lb in self.original_edge_refs:
edge = self.graph[u][idx]
flow_in_additional = (edge[3][2] - edge[3][1]) - edge[1]
actual_flow = lb + flow_in_additional
flows.append(actual_flow)
if self.visualize:
self.visualize_final_flow(flows)
plt.pause(5.0)
return flows
def get_edge_description(self, u, v):
"""获取边的描述信息"""
if u == self.s:
return f"s → {v}"
elif v == self.t:
return f"{u} → t"
elif (u, v) in self.edge_info:
info = self.edge_info[(u, v)]['info']
if isinstance(info, tuple) and len(info) > 3:
return f"{u} → {v} ({info[3]})"
return f"{u} → {v}"
def initialize_visualization(self):
"""初始化可视化布局"""
self.G = nx.DiGraph()
# 添加节点
for i in range(self.n):
self.G.add_node(i, label=f"{i}")
self.G.add_node(self.s, label="s")
self.G.add_node(self.t, label="t")
# 添加边
for u in range(self.total_nodes):
for edge in self.graph[u]:
v, cap, _, info = edge
if cap > 0: # 只添加正向边
self.G.add_edge(u, v, capacity=cap, flow=0)
# 创建环形布局
self.pos = {}
# 普通节点布置在圆上
angles = np.linspace(0, 2 * np.pi, self.n, endpoint=False)
for i in range(self.n):
angle = angles[i]
self.pos[i] = (np.cos(angle), np.sin(angle))
# 超级源点和汇点
self.pos[self.s] = (0, 1.5) # 上方
self.pos[self.t] = (0, -1.5) # 下方
# 初始绘图
self.ax.clear()
nx.draw_networkx_nodes(self.G, self.pos, node_size=800,
node_color=['lightblue' if n < self.n else 'salmon' for n in self.G.nodes()])
nx.draw_networkx_labels(self.G, self.pos,
labels={n: d['label'] for n, d in self.G.nodes(data=True)})
# 绘制边
self.edge_collection = nx.draw_networkx_edges(
self.G, self.pos, arrowstyle='->', arrowsize=20,
edge_color='gray', width=1, ax=self.ax
)
# 初始化边标签
self.edge_labels = {}
for u, v in self.G.edges():
self.edge_labels[(u, v)] = self.ax.text(0, 0, "",
fontsize=8, ha='center', va='center')
self.ax.set_title("初始化网络", fontsize=14)
self.ax.set_axis_off()
plt.tight_layout()
plt.pause(2.0)
def visualize_step(self, message):
"""可视化当前步骤"""
self.ax.clear()
# 绘制节点
nx.draw_networkx_nodes(self.G, self.pos, node_size=800,
node_color=['lightblue' if n < self.n else 'salmon' for n in self.G.nodes()])
nx.draw_networkx_labels(self.G, self.pos,
labels={n: d['label'] for n, d in self.G.nodes(data=True)})
# 绘制边并设置颜色和宽度
edge_colors = []
edge_widths = []
for u, v in self.G.edges():
# 获取当前边的状态
cap = self.G[u][v]['capacity']
flow = self.edge_info.get((u, v), {}).get('flow', 0)
# 计算饱和度
saturation = flow / cap if cap > 0 else 0
# 使用颜色表示饱和度
edge_colors.append(plt.cm.RdYlGn(saturation))
# 使用宽度表示流量
edge_widths.append(1 + 3 * saturation)
# 绘制边
nx.draw_networkx_edges(
self.G, self.pos, arrowstyle='->', arrowsize=20,
edge_color=edge_colors, width=edge_widths, ax=self.ax
)
# 更新边标签
for (u, v), text in self.edge_labels.items():
# 获取边信息
cap = self.G[u][v]['capacity']
flow = self.edge_info.get((u, v), {}).get('flow', 0)
# 特殊边处理
if u == self.s or v == self.t:
label = f"{flow}/{cap}"
else:
# 获取原始边信息
info = self.edge_info.get((u, v), {}).get('info', None)
if info and isinstance(info, tuple):
_, lb, ub, name = info
actual_flow = lb + flow
label = f"{name}: {actual_flow}/{ub}\n[{lb},{ub}]"
else:
label = f"{flow}/{cap}"
# 计算边的中点位置
x = (self.pos[u][0] + self.pos[v][0]) / 2
y = (self.pos[u][1] + self.pos[v][1]) / 2
# 更新文本位置和内容
text.set_position((x, y))
text.set_text(label)
self.ax.add_artist(text)
# 显示当前信息
self.ax.set_title(message, fontsize=14)
self.ax.set_axis_off()
plt.tight_layout()
plt.draw()
def visualize_final_flow(self, flows):
"""可视化最终可行流分配"""
self.ax.clear()
# 只绘制原图节点
H = nx.DiGraph()
for i in range(self.n):
H.add_node(i, label=f"{i}")
# 添加原图边
for i, (u, v, lb, ub) in enumerate(self.original_edges):
H.add_edge(u, v, flow=flows[i], lb=lb, ub=ub, name=f"e{i}")
# 环形布局
angles = np.linspace(0, 2 * np.pi, self.n, endpoint=False)
pos = {}
for i in range(self.n):
angle = angles[i]
pos[i] = (np.cos(angle), np.sin(angle))
# 绘制节点
nx.draw_networkx_nodes(H, pos, node_size=800, node_color='lightgreen')
nx.draw_networkx_labels(H, pos)
# 绘制边
edge_colors = []
edge_widths = []
for u, v in H.edges():
flow = H[u][v]['flow']
ub = H[u][v]['ub']
saturation = flow / ub
edge_colors.append(plt.cm.RdYlGn(saturation))
edge_widths.append(1 + 3 * saturation)
nx.draw_networkx_edges(
H, pos, arrowstyle='->', arrowsize=20,
edge_color=edge_colors, width=edge_widths, ax=self.ax
)
# 添加边标签
edge_labels = {}
for u, v in H.edges():
flow = H[u][v]['flow']
lb = H[u][v]['lb']
ub = H[u][v]['ub']
name = H[u][v]['name']
edge_labels[(u, v)] = f"{name}: {flow}\n[{lb},{ub}]"
nx.draw_networkx_edge_labels(H, pos, edge_labels=edge_labels, font_size=8)
self.ax.set_title("可行流分配结果", fontsize=14)
self.ax.set_axis_off()
plt.tight_layout()
plt.draw()
def circulation_flow_visual(n, edges):
"""无源汇上下界可行流求解与可视化"""
# 创建可视化实例
dinic_visual = DinicVisual(n, edges, visualize=True)
# 计算可行流
flows = dinic_visual.max_flow()
if flows is None:
print("无可行流解")
return None
print("\n各边实际流量分配:")
for i, (u, v, lb, ub) in enumerate(edges):
print(f"边 {u}→{v} ({lb},{ub}): {flows[i]}")
plt.show() # 保持窗口打开
return flows
if __name__ == "__main__":
# 15节点复杂网络示例
print("=" * 50)
print("15节点复杂网络的无源汇上下界可行流计算")
# 定义15节点网络的边
n = 15 # 节点数
edges = [
# 核心环状结构 (0-4)
(0, 1, 2, 5), (1, 2, 1, 4), (2, 3, 3, 6), (3, 4, 2, 5),
(4, 0, 1, 3), (0, 3, 1, 4), (2, 0, 2, 5),
# 中间层级连接 (5-9)
(1, 5, 1, 3), (5, 6, 2, 4), (6, 7, 1, 3), (7, 8, 3, 6),
(8, 9, 2, 5), (9, 2, 1, 4), (3, 7, 1, 3), (4, 8, 2, 5),
# 外围节点连接 (10-14)
(5, 10, 1, 3), (10, 11, 2, 4), (11, 12, 1, 3), (6, 11, 1, 4),
(7, 13, 2, 5), (13, 14, 1, 3), (14, 9, 1, 4), (12, 8, 2, 5),
(12, 14, 1, 3)
]
# 计算并可视化可行流
flows = circulation_flow_visual(n, edges)
代码运行结果是
C:\Users\25827\.conda\envs\torch\python.exe C:\Users\25827\Desktop\图论代码\无源汇上下界可行流.py
==================================================
15节点复杂网络的无源汇上下界可行流计算
无可行流解
进程已结束,退出代码为 0
1.给出一个有解的例子,但是网络框架不变
2.增强访问和探索可视化(比如访问路径)
给出修改后完整全流程代码(没有省略)
最新发布