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()
输出(遍历的边和这些边的组合权重)

可视化是对无向图的粗略搜索,但它仍然为您提供了解决方案的要点


您正在实现的逻辑与逻辑相同吗?您正在实现的逻辑与逻辑相同吗?