Skip to content

Commit 5929672

Browse files
committed
Come to an end for now
1 parent b700167 commit 5929672

17 files changed

+160
-29
lines changed

README.md

+3
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,6 @@
2525
### 3.1 Hungary algorithm
2626
- [代码](graph_match/hungary.py)
2727
- [图例](graph_match/hungary.png)
28+
### 3.2 Kuhn_Munkras
29+
- [代码](graph_match/kuhn_munkras.py)
30+
- [图例](graph_match/kuhn_munkras.png)

graph_match/hungary.png

15.6 KB
Loading

graph_match/hungary.py

+9-12
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@
44

55
class Hungary:
66
def __init__(self, nodes_one, nodes_two, edges) -> None:
7-
self.nodes_one = nodes_one
8-
self.nodes_two = nodes_two
9-
self.edges = edges
10-
7+
self.nodes_one = nodes_one # 二部图的第一部分节点
8+
self.nodes_two = nodes_two # 二部图的第二部分节点
9+
self.edges = edges # 二部图的所有边
10+
1111
def max_match(self):
1212
'''求最大匹配边的数量,标记匹配的节点
1313
'''
14-
match = defaultdict(lambda: -1) #记录节点匹配
14+
match = defaultdict(lambda: None) #记录节点匹配
1515
num_match = 0 #最大匹配数量
1616
node_neighbors = defaultdict(list) # 节点邻居映射
17-
1817
for (u, v) in self.edges:
1918
node_neighbors[u].append(v)
2019
node_neighbors[v].append(u)
@@ -23,11 +22,10 @@ def dfs(u):
2322
for v in node_neighbors[u]:
2423
if v not in visited:
2524
visited.add(v)
26-
if match[v] == -1 or dfs(match[v]):
25+
if match[v] == None or dfs(match[v]):
2726
match[v] = u
2827
return True
2928
return False
30-
3129
for node in self.nodes_one:
3230
visited = set()
3331
if (dfs(node)):
@@ -64,9 +62,10 @@ def draw(G, nodes_one, nodes_two, color_edges):
6462
one_y -= 1
6563

6664
for node_two in nodes_two:
67-
pos[node_two] = [two_x // 3, two_y]
65+
pos[node_two] = [two_x, two_y]
6866
two_y -= 1
6967
# print(pos)
68+
plt.title('Hungary Algorithm: Maximum Matching')
7069
nx.draw(G, pos, with_labels=True, node_color=node_color, edge_color=edge_color)
7170
plt.savefig('hungary.png', format='PNG')
7271
plt.show()
@@ -86,9 +85,7 @@ def main():
8685
edges = [(0, 'a'), (0, 'b'), (1, 'b'), (1, 'c'), (2, 'a'), (2, 'b'), (3, 'c'), (3, 'd')]
8786

8887
num_match, match = Hungary(nodes_one, nodes_two, edges).max_match()
89-
match_edges = []
90-
for key, value in match.items():
91-
match_edges.append((value, key))
88+
match_edges = [(u, v) for u, v in match.items()]
9289
print('{} | {}'.format(match_edges, num_match))
9390

9491
G = nx.Graph()

graph_match/kuhn_munkras.png

25.2 KB
Loading

graph_match/kuhn_munkras.py

+127
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import networkx as nx
2+
import matplotlib.pyplot as plt
3+
import sys
4+
from collections import defaultdict
5+
6+
7+
class KM:
8+
def __init__(self, nodes_one, nodes_two, edges) -> None:
9+
self.nodes_one = nodes_one # 二部图第一部分节点
10+
self.nodes_two = nodes_two # 二部图第二部分节点
11+
self.edges = edges # 所有边(带权重)
12+
13+
def optimal_match(self):
14+
'''理解该算法建议手动模拟一下代码中的例子
15+
'''
16+
match = defaultdict(lambda: None) # 记录节点匹配情况
17+
sum_weights = 0 # 最优匹配权重总和
18+
node_neighbors = defaultdict(list) # 节点邻居映射
19+
edge_weight = defaultdict(int) # 边权重映射
20+
21+
label_one = defaultdict(int) # 第一部分节点的可行顶标
22+
label_two = defaultdict(int) # 第二部分节点的可行顶标
23+
slack = defaultdict(lambda: sys.maxsize) # 记录第二部分节点能和第一部分节点匹配还需要多少权重
24+
one_visited, two_visited = set(), set()
25+
# 初始化
26+
for (u, v, w) in self.edges:
27+
node_neighbors[u].append(v)
28+
node_neighbors[v].append(u)
29+
edge_weight[(u, v)] = edge_weight[(v, u)] = w
30+
for node_one in self.nodes_one:
31+
max_w = 0
32+
for node_one_neighbor in node_neighbors[node_one]:
33+
max_w = max(max_w, edge_weight[(node_one, node_one_neighbor)])
34+
label_one[node_one] = max_w
35+
36+
def dfs(u):
37+
one_visited.add(u)
38+
39+
for v in node_neighbors[u]:
40+
if v in two_visited:
41+
continue
42+
gap = label_one[u]+label_two[v] - edge_weight[(u, v)]
43+
44+
if gap == 0:
45+
two_visited.add(v)
46+
if match[v] == None or dfs(match[v]):
47+
match[v] = u
48+
return True #
49+
else:
50+
slack[v] = min(slack[v], gap)
51+
return False
52+
53+
for u in self.nodes_one:
54+
slack.clear()
55+
while not dfs(u):
56+
min_gap = sys.maxsize # 最小可以降低多少顶标值能够完成匹配
57+
for node_two in self.nodes_two:
58+
if node_two not in two_visited:
59+
min_gap = min(min_gap, slack[node_two])
60+
for node_one in self.nodes_one:
61+
if node_one in one_visited:
62+
label_one[node_one] -= min_gap
63+
for node_two in self.nodes_two:
64+
if node_two in two_visited:
65+
label_two[node_two] += min_gap
66+
else:
67+
slack[node_two] -= min_gap
68+
one_visited.clear()
69+
two_visited.clear()
70+
# 求最优匹配的权重和
71+
for key, value in match.items():
72+
sum_weights += edge_weight[(value, key)]
73+
return match, sum_weights
74+
75+
def draw(G, nodes_one, nodes_two, color_edges):
76+
nodes = list(G.nodes)
77+
edges = list(G.edges)
78+
num_node = len(nodes)
79+
num_edge = len(edges)
80+
node_color = ['b'] * num_node
81+
edge_color = ['b'] * num_edge
82+
83+
for i in range(0, num_node):
84+
if isinstance(nodes[i], type(nodes_one[0])):
85+
node_color[i] = 'r'
86+
87+
for i in range(num_edge):
88+
u, v = edges[i][0], edges[i][1]
89+
# 无向图
90+
if (u, v) in color_edges or (v, u) in color_edges:
91+
edge_color[i] = 'r'
92+
'''
93+
自定义pos
94+
'''
95+
# 对matplotlib不太熟悉,布局有待改进
96+
pos = dict()
97+
size = max(len(nodes_one), len(nodes_two)) + 2
98+
one_x, two_x = size//3, 2*size // 3
99+
one_y, two_y = size-1, size-1
100+
for node_one in nodes_one:
101+
pos[node_one] = [one_x, one_y]
102+
one_y -= 1
103+
104+
for node_two in nodes_two:
105+
pos[node_two] = [two_x, two_y]
106+
two_y -= 1
107+
plt.title('KM Algorithm: Optimal Matching')
108+
nx.draw(G, pos, with_labels=True, node_color=node_color, edge_color=edge_color)
109+
edge_labels = nx.get_edge_attributes(G, 'weight')
110+
# label_pos=0.8作用: 防止图中交叉边上权重值重叠
111+
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, label_pos=0.8)
112+
plt.savefig('kuhn_munkras.png', format='PNG')
113+
plt.show()
114+
def main():
115+
nodes_one = [0, 1, 2]
116+
nodes_two = ['a', 'b', 'c']
117+
edges = [(0, 'a', 3), (0, 'c', 4), (1, 'a', 2), (1, 'b', 1), (1, 'c', 3), (2, 'c', 5)]
118+
match, sum_weights = KM(nodes_one, nodes_two, edges).optimal_match()
119+
match_edges = [(u, v) for u, v in match.items()]
120+
print('{} | {}'.format(match_edges, sum_weights))
121+
G = nx.Graph()
122+
G.add_nodes_from(nodes_one+nodes_two)
123+
G.add_weighted_edges_from(edges)
124+
draw(G, nodes_one, nodes_two, match_edges)
125+
126+
if __name__ == '__main__':
127+
main()

mst/broken_circle.png

-4.1 KB
Loading

mst/broken_circle.py

+1
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ def draw(G, color_edges):
9797

9898
# 边的长度和权重大小成正比
9999
pos = nx.kamada_kawai_layout(G)
100+
plt.title('MST')
100101
nx.draw(G, pos, with_labels=True, edge_color=edge_color)
101102
edge_labels = nx.get_edge_attributes(G, 'weight')
102103
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)

mst/kruskal.png

-4.1 KB
Loading

mst/kruskal.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,11 @@ def draw(G, color_edges):
5454
if (u, v) in color_edges or (v, u) in color_edges:
5555
edge_color[i] = 'r'
5656
pos = nx.kamada_kawai_layout(G)
57+
plt.title('MST')
5758
nx.draw(G, pos, with_labels=True, edge_color=edge_color)
5859
edge_labels = nx.get_edge_attributes(G, 'weight')
5960
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
60-
# plt.savefig("kruskal.png", format="PNG")
61+
plt.savefig("kruskal.png", format="PNG")
6162
plt.show()
6263

6364
def main():

mst/prim.png

648 Bytes
Loading

mst/prim.py

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ def draw(G, color_edges):
8080

8181
# 边的长度和权重大小成正比
8282
pos = nx.kamada_kawai_layout(G)
83+
plt.title('MST')
8384
edge_labels = nx.get_edge_attributes(G, 'weight') # 画权重
8485
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels)
8586
nx.draw(G, pos, with_labels=True, edge_color=edge_color) # 画边

shortest_path/bellman_ford.png

-2.83 KB
Loading

shortest_path/bellman_ford.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -102,10 +102,10 @@ def draw(DG, color_nodes, color_edges):
102102
edge_color[i] = 'r'
103103

104104
pos = nx.circular_layout(DG)
105-
plt.title('Digraph-Bellman_Ford')
105+
plt.title('Digraph: Shortest Path')
106106
nx.draw(DG, pos, with_labels=True, node_color=node_color, edge_color=edge_color) # 画图
107107
edge_labels = nx.get_edge_attributes(DG, 'weight')
108-
edge_labels = { (key[0],key[1]): "w:"+str(edge_labels[key]) for key in edge_labels }
108+
# edge_labels = { (key[0],key[1]): "w:"+str(edge_labels[key]) for key in edge_labels }
109109
nx.draw_networkx_edge_labels(DG, pos, edge_labels=edge_labels) # 画权重
110110
plt.savefig('bellman_ford.png', format='PNG')
111111
plt.show()

shortest_path/dijkstra.png

-1.64 KB
Loading

shortest_path/dijkstra.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -108,10 +108,10 @@ def draw(DG, color_nodes, color_edges):
108108
edge_color[i] = 'r'
109109

110110
pos = nx.circular_layout(DG)
111-
plt.title('Digraph-Dijkstra')
111+
plt.title('Digraph: Shortest Path')
112112
nx.draw(DG, pos, with_labels=True, node_color=node_color, edge_color=edge_color) # 画图
113113
edge_labels = nx.get_edge_attributes(DG, 'weight')
114-
edge_labels = { (key[0],key[1]): "w:"+str(edge_labels[key]) for key in edge_labels }
114+
# edge_labels = { (key[0],key[1]): "w:"+str(edge_labels[key]) for key in edge_labels }
115115
nx.draw_networkx_edge_labels(DG, pos, edge_labels=edge_labels) # 画权重
116116
plt.savefig('dijkstra.png', format='PNG')
117117
plt.show()

shortest_path/floyd_warshall.png

-2.79 KB
Loading

shortest_path/floyd_warshall.py

+13-12
Original file line numberDiff line numberDiff line change
@@ -11,24 +11,25 @@
1111

1212
class FloydWarshall:
1313
def __init__(self, graph_mat) -> None:
14-
self.graph_mat = graph_mat
14+
self.graph_mat = graph_mat # 图的邻接矩阵
1515

1616
def shortest_path(self):
1717
m, n = len(self.graph_mat), len(self.graph_mat[0])
18-
path = [[-1] * n for _ in range(m)]
18+
distance_mat = self.graph_mat.copy()
19+
path = [[None] * n for _ in range(m)] # 记录路径
1920
for k in range(m):
2021
for i in range(m):
2122
for j in range(n):
22-
if self.graph_mat[i][k]+self.graph_mat[k][j] < self.graph_mat[i][j]:
23-
self.graph_mat[i][j] = self.graph_mat[i][k]+self.graph_mat[k][j]
23+
if distance_mat[i][k]+distance_mat[k][j] < distance_mat[i][j]:
24+
distance_mat[i][j] = distance_mat[i][k]+distance_mat[k][j]
2425
path[i][j] = k
25-
return self.graph_mat, path
26+
return distance_mat, path
2627
# 递归
2728
# def get_nodes_edges_recursion(paths, i, j):
2829
# if i == j:
2930
# return [i], []
3031
# else:
31-
# if paths[i][j] == -1:
32+
# if paths[i][j] == None:
3233
# return [j], [(i, j)]
3334
# else:
3435
# left_nodes, left_edges = get_nodes_edges(paths, i, paths[i][j])
@@ -38,7 +39,7 @@ def shortest_path(self):
3839
def get_nodes_edges(paths, i, j):
3940
pass_nodes = [i, j]
4041
pass_edges = []
41-
while paths[i][j] != -1:
42+
while paths[i][j] != None:
4243
j = paths[i][j]
4344
pass_nodes.insert(1, j)
4445
pass_edges.append((j, pass_nodes[2]))
@@ -67,7 +68,7 @@ def draw(sp_mat, color_nodes, color_edges):
6768
# print(nx_G.edges(data=True))
6869
edge_labels = nx.get_edge_attributes(nx_G, 'w')
6970
# tensor.item() tensor(0.)->0(tensor->int)
70-
edge_labels = { (key[0],key[1]): "w:"+str(edge_labels[key].item()) for key in edge_labels }
71+
edge_labels = { (key[0],key[1]): edge_labels[key].item() for key in edge_labels }
7172
edges = list(nx_G.edges)
7273
num_nodes = nx_G.number_of_nodes()
7374
num_edges = nx_G.number_of_edges()
@@ -82,7 +83,7 @@ def draw(sp_mat, color_nodes, color_edges):
8283
if (u, v) in set(color_edges) or (v, u) in set(color_edges):
8384
edge_color[i] = 'r'
8485
pos = nx.circular_layout(nx_G)
85-
plt.title('Undigraph-Floyd Warshall')
86+
plt.title('Undigraph: Shortest Path')
8687
nx.draw(nx_G, pos, with_labels=True, node_color=node_color, edge_color=edge_color)
8788
nx.draw_networkx_edge_labels(nx_G, pos, edge_labels=edge_labels)
8889
plt.savefig('floyd_warshall.png', format='PNG')
@@ -105,13 +106,13 @@ def main():
105106
graph_mat[i][j] += graph_mat[j][i]
106107
graph_mat[j][i] = graph_mat[i][j]
107108

108-
finaly_mat, paths = FloydWarshall(graph_mat).shortest_path()
109-
print(finaly_mat)
109+
distance_mat, paths = FloydWarshall(graph_mat).shortest_path()
110+
print(distance_mat)
110111
print(paths)
111112

112113
for i in range(count_node):
113114
for j in range(i+1, count_node):
114-
print('{}->{}: {} | {}'.format(i, j, get_nodes_edges(paths, i, j), finaly_mat[i][j]))
115+
print('{}->{}: {} | {}'.format(i, j, get_nodes_edges(paths, i, j), distance_mat[i][j]))
115116

116117
start_node, end_node = 0, 5 # 开始节点,结束节点
117118
pass_nodes, pass_edges = get_nodes_edges(paths, start_node, end_node)

0 commit comments

Comments
 (0)