Python 修正加权图
我已经编写了一个代码来遍历无向非加权图。现在我想让这段代码用于加权图,其中权重将决定节点之间的距离,我的代码将给出起始节点和结束节点之间的最短路径。我无法获得代码的逻辑。谁能帮帮我吗Python 修正加权图,python,shortest-path,breadth-first-search,weighted-graph,Python,Shortest Path,Breadth First Search,Weighted Graph,我已经编写了一个代码来遍历无向非加权图。现在我想让这段代码用于加权图,其中权重将决定节点之间的距离,我的代码将给出起始节点和结束节点之间的最短路径。我无法获得代码的逻辑。谁能帮帮我吗 graph = {'A': ['B', 'C', 'E'], 'B': ['A','D', 'E'], 'C': ['A', 'F', 'G'], 'D': ['B'], 'E': ['A', 'B','D'], 'F': [
graph = {'A': ['B', 'C', 'E'],
'B': ['A','D', 'E'],
'C': ['A', 'F', 'G'],
'D': ['B'],
'E': ['A', 'B','D'],
'F': ['C'],
'G': ['C']}
def undirected_graph(graph,start,stop):
visited = []
queue = [start]
if start == stop:
print("Woah we ended before even starting!")
else:
while queue:
path = queue.pop(0)
node = path[-1]
if node not in visited:
visited.append(node)
neighbours = graph[node]
for neighbour in neighbours:
new_list = list(path)
new_list.append(neighbour)
queue.append(new_list)
if neighbour == stop:
print("We reached the end")
return new_list
undirected_graph(graph,'A','G')
networkx
模块允许您创建图形并找到最短路径(Dijkstra方法)。它与Anaconda发行版一起安装,否则请使用pip安装
以下是一个例子:
将networkx导入为nx
作为pd进口熊猫
data=pd.read_excel('network.xlsx')#一些图形
数据
输出:
Origin Destination Distance
0 A B 10
1 A C 20
2 A D 15
3 B D 10
4 B E 5
5 C F 20
6 C G 20
7 C D 15
8 D G 20
df=nx.from\u pandas\u edgelist(数据,source='Origin',target='Destination',edge\u attr=True)
nx.dijkstra_路径(df,source='E',target='F',weight='Distance')
输出:
Origin Destination Distance
0 A B 10
1 A C 20
2 A D 15
3 B D 10
4 B E 5
5 C F 20
6 C G 20
7 C D 15
8 D G 20
['E','B','D','C','F']
networkx
模块提供了更多:
例如,您可以绘制网络:
nx.draw_networkx(df, with_labels=True)
通过
networkx
模块,您可以创建图形并找到最短路径(Dijkstra方法)。它与Anaconda发行版一起安装,否则请使用pip安装
以下是一个例子:
将networkx导入为nx
作为pd进口熊猫
data=pd.read_excel('network.xlsx')#一些图形
数据
输出:
Origin Destination Distance
0 A B 10
1 A C 20
2 A D 15
3 B D 10
4 B E 5
5 C F 20
6 C G 20
7 C D 15
8 D G 20
df=nx.from\u pandas\u edgelist(数据,source='Origin',target='Destination',edge\u attr=True)
nx.dijkstra_路径(df,source='E',target='F',weight='Distance')
输出:
Origin Destination Distance
0 A B 10
1 A C 20
2 A D 15
3 B D 10
4 B E 5
5 C F 20
6 C G 20
7 C D 15
8 D G 20
['E','B','D','C','F']
networkx
模块提供了更多:
例如,您可以绘制网络:
nx.draw_networkx(df, with_labels=True)
您正在寻找Dijkstra的最短路径算法。巧合的是,就在本周,我用Python实现了这一点。实现有点繁重(因为我的目标是展示如何使用
unittest
来执行单元测试),但它仍然有效。Dijkstra的算法适用于有向图,但您可以通过为每个A--B
无向边创建A->B
和B->A
有向边,将无向图转换为有向图,就像您所做的那样
from collections import defaultdict
from itertools import product, chain
class DijkstraNegativeWeightException(Exception):
pass
class DijkstraDisconnectedGraphException(Exception):
pass
class Dijkstra:
def __init__(self, graph_data, source):
self._graph = defaultdict(dict, graph_data)
self._check_edge_weights()
self.reset_source(source)
self._solved = False
@property
def edges(self):
return [(i, j) for i in self._graph for j in self._graph[i]]
@property
def nodes(self):
return list(set(chain(*self.edges)))
def _check_source_in_nodes(self):
msg = 'Source node \'{}\' not in graph.'
if self._source not in self.nodes:
raise ValueError(msg.format(self._source))
def _check_edge_weights(self):
msg = 'Graph has negative weights, but weights must be non-negative.'
if any(self._graph[i][j] < 0 for (i, j) in self.edges):
raise DijkstraNegativeWeightException(msg)
def reset_source(self, source):
self._source = source
self._check_source_in_nodes()
self._solution_x = []
self._solution_z = {source: 0}
self._visited = set([source])
self._unvisited = set()
for key, val in self._graph.items():
self._unvisited.add(key)
self._unvisited.update(val.keys())
self._unvisited.difference_update(self._visited)
self._solved = False
def run(self):
weight_candidates = self._graph[self._source].copy()
node_candidates = dict(product(weight_candidates.keys(),
(self._source,)))
while node_candidates:
j = min(weight_candidates, key=weight_candidates.get)
weight_best, i = weight_candidates.pop(j), node_candidates.pop(j)
for k in self._graph[j].keys() & self._unvisited:
weight_next = self._graph[j][k]
if (k not in node_candidates
or weight_candidates[k] > weight_best + weight_next):
weight_candidates[k] = weight_best + weight_next
node_candidates[k] = j
self._solution_x.append((i, j))
self._solution_z[j] = weight_best
self._visited |= {j}
self._unvisited -= {j}
self._solved = True
def path_to(self, target):
if self._source in self._visited and target in self._unvisited:
msg = 'No path from {} to {}; graph is disconnected.'
msg = msg.format(self._visited, self._unvisited)
raise DijkstraDisconnectedGraphException(msg)
solution = self._solution_x.copy()
path = []
while solution:
i, j = solution.pop()
if j == target:
path.append((i, j))
break
while solution:
i_prev, _, i, j = *path[-1], *solution.pop()
if j == i_prev:
path.append((i, j))
if i == self._source:
break
return list(reversed(path)), self._solution_z[target]
def visualize(self, source=None, target=None):
import networkx as nx
import matplotlib.pyplot as plt
if (source is not None and source != self._source):
self.reset_source(source)
if not self._solved:
self.run()
if target is not None:
path, _ = self.path_to(target=target)
else:
path = self._solution_x
edgelist = self.edges
nodelist = self.nodes
nxgraph = nx.DiGraph()
nxgraph.add_edges_from(edgelist)
weights = {(i, j): self._graph[i][j] for (i, j) in edgelist}
found = list(chain(*path))
ncolors = ['springgreen' if node in found else 'lightcoral'
for node in nodelist]
ecolors = ['dodgerblue' if edge in path else 'black'
for edge in edgelist]
sizes = [3 if edge in path else 1 for edge in edgelist]
pos = nx.kamada_kawai_layout(nxgraph)
nx.draw_networkx(nxgraph, pos=pos,
nodelist=nodelist, node_color=ncolors,
edgelist=edgelist, edge_color=ecolors, width=sizes)
nx.draw_networkx_edge_labels(nxgraph, pos=pos, edge_labels=weights)
plt.axis('equal')
plt.show()
输出(遍历的边和这些边的组合权重)
可视化是对无向图的粗略搜索,但它仍然为您提供了解决方案的要点
您正在寻找Dijkstra的最短路径算法。巧合的是,就在本周,我用Python实现了这一点。实现有点繁重(因为我的目标是展示如何使用
unittest
来执行单元测试),但它仍然有效。Dijkstra的算法适用于有向图,但您可以通过为每个A--B
无向边创建A->B
和B->A
有向边,将无向图转换为有向图,就像您所做的那样
from collections import defaultdict
from itertools import product, chain
class DijkstraNegativeWeightException(Exception):
pass
class DijkstraDisconnectedGraphException(Exception):
pass
class Dijkstra:
def __init__(self, graph_data, source):
self._graph = defaultdict(dict, graph_data)
self._check_edge_weights()
self.reset_source(source)
self._solved = False
@property
def edges(self):
return [(i, j) for i in self._graph for j in self._graph[i]]
@property
def nodes(self):
return list(set(chain(*self.edges)))
def _check_source_in_nodes(self):
msg = 'Source node \'{}\' not in graph.'
if self._source not in self.nodes:
raise ValueError(msg.format(self._source))
def _check_edge_weights(self):
msg = 'Graph has negative weights, but weights must be non-negative.'
if any(self._graph[i][j] < 0 for (i, j) in self.edges):
raise DijkstraNegativeWeightException(msg)
def reset_source(self, source):
self._source = source
self._check_source_in_nodes()
self._solution_x = []
self._solution_z = {source: 0}
self._visited = set([source])
self._unvisited = set()
for key, val in self._graph.items():
self._unvisited.add(key)
self._unvisited.update(val.keys())
self._unvisited.difference_update(self._visited)
self._solved = False
def run(self):
weight_candidates = self._graph[self._source].copy()
node_candidates = dict(product(weight_candidates.keys(),
(self._source,)))
while node_candidates:
j = min(weight_candidates, key=weight_candidates.get)
weight_best, i = weight_candidates.pop(j), node_candidates.pop(j)
for k in self._graph[j].keys() & self._unvisited:
weight_next = self._graph[j][k]
if (k not in node_candidates
or weight_candidates[k] > weight_best + weight_next):
weight_candidates[k] = weight_best + weight_next
node_candidates[k] = j
self._solution_x.append((i, j))
self._solution_z[j] = weight_best
self._visited |= {j}
self._unvisited -= {j}
self._solved = True
def path_to(self, target):
if self._source in self._visited and target in self._unvisited:
msg = 'No path from {} to {}; graph is disconnected.'
msg = msg.format(self._visited, self._unvisited)
raise DijkstraDisconnectedGraphException(msg)
solution = self._solution_x.copy()
path = []
while solution:
i, j = solution.pop()
if j == target:
path.append((i, j))
break
while solution:
i_prev, _, i, j = *path[-1], *solution.pop()
if j == i_prev:
path.append((i, j))
if i == self._source:
break
return list(reversed(path)), self._solution_z[target]
def visualize(self, source=None, target=None):
import networkx as nx
import matplotlib.pyplot as plt
if (source is not None and source != self._source):
self.reset_source(source)
if not self._solved:
self.run()
if target is not None:
path, _ = self.path_to(target=target)
else:
path = self._solution_x
edgelist = self.edges
nodelist = self.nodes
nxgraph = nx.DiGraph()
nxgraph.add_edges_from(edgelist)
weights = {(i, j): self._graph[i][j] for (i, j) in edgelist}
found = list(chain(*path))
ncolors = ['springgreen' if node in found else 'lightcoral'
for node in nodelist]
ecolors = ['dodgerblue' if edge in path else 'black'
for edge in edgelist]
sizes = [3 if edge in path else 1 for edge in edgelist]
pos = nx.kamada_kawai_layout(nxgraph)
nx.draw_networkx(nxgraph, pos=pos,
nodelist=nodelist, node_color=ncolors,
edgelist=edgelist, edge_color=ecolors, width=sizes)
nx.draw_networkx_edge_labels(nxgraph, pos=pos, edge_labels=weights)
plt.axis('equal')
plt.show()
输出(遍历的边和这些边的组合权重)
可视化是对无向图的粗略搜索,但它仍然为您提供了解决方案的要点
您正在实现的逻辑与逻辑相同吗?您正在实现的逻辑与逻辑相同吗?