Python 在使用networkx计算具有负权重的最短路径时,如何处理负成本周期错误?

Python 在使用networkx计算具有负权重的最短路径时,如何处理负成本周期错误?,python,networkx,graph-theory,shortest-path,Python,Networkx,Graph Theory,Shortest Path,我正在使用networkx解决最短路径问题。我希望能够使用负权重图(负权重表示特定数量的增加,正权重表示消耗) 左边的图片显示了最小化“长度”属性时的最佳路线,右边的图片是我希望在最小化带有负值的“高度”属性时获得的路线 当我尝试用bellman-ford方法运行最短路径算法时(见下面的代码),我得到了错误NetworkXUnbounded:检测到负成本周期。 import matplotlib.pyplot as plt from itertools import combinations,

我正在使用networkx解决最短路径问题。我希望能够使用负权重图(负权重表示特定数量的增加,正权重表示消耗)

左边的图片显示了最小化“长度”属性时的最佳路线,右边的图片是我希望在最小化带有负值的“高度”属性时获得的路线

当我尝试用bellman-ford方法运行最短路径算法时(见下面的代码),我得到了错误
NetworkXUnbounded:检测到负成本周期。

import matplotlib.pyplot as plt
from itertools import combinations, groupby
import os
import numpy as np
import networkx as nx
import random


# 1. Define test network 
MG = nx.MultiDiGraph()
MG.add_edges_from([("B", "A", {"length": 0.8}), ("A", "B", {"length": 1.}), ("D", "G", {"length": 3.5}),
                   ("B", "D", {"length": 20.8}), ("A", "C", {"length": 9.7}), ("D", "C", {"length": 0.3}),
                   ("B", "E", {"length": 4.8}), ("D", "E", {"length": 0.05}), ("C", "E", {"length": 0.1}),          
                   ("E", "C", {"length": 0.7}), ("E", "F", {"length": 0.4}), ("E", "G", {"length": 15.}),           
                   ("F", "C", {"length": 0.9}), ("F", "D", {"length": 4.}),                       
                  ])
attrs = {'B': {"x": -20., "y": 60.}, 'A': {"x": 28., "y":55.},
         'C': {"x": -12., "y": 40.}, 'D': {"x": 40., "y":45.},
         'E': {"x": 8., "y": 35.}, 'F': {"x": -8., "y":15.},    
         'G': {"x": 21., "y":5.},    
        }

for num, (k,v) in enumerate(attrs.items()):
    attrs[k]={**v,
             }  
nx.set_node_attributes(MG, attrs)

rng = np.random.default_rng(seed=42)
random_height = list(rng.uniform(low=-100, high=100, size=len(MG.edges)).round(0))
attrs={}
for num, edge in enumerate(MG.edges):
    attrs[edge]={'height': random_height[num]}
nx.set_edge_attributes(MG, attrs)


# 2. Calculate shortest route
best_route_by_length = nx.shortest_path(MG, "A", "G",weight="length")
print(f"Best route by length: {best_route_by_length}")

best_route_by_height = nx.shortest_path(MG, "A", "G",weight="height",method='bellman-ford')
print(f"Best route by height: {best_route_by_height}")
是否有解决此问题的方法(例如,拆卸周期)


编辑:我正在寻找没有周期的最佳解决方案

解决此问题的一个解决方案是:

  • 创建一份我的图形副本,其中所有边属性<代码>高度都以最小的负值移动(这样我的所有权重都为正值)
  • 计算了此新图形上的最短路径
  • 但这并不是很简单,因为它需要处理两个图或计算额外的转换

    我在下面附上了一些代码,展示了如何计算原始图片中的紫色路径

    导入副本
    MH=复制。深度复制(MG)
    属性={}
    对于num,枚举中的边(MH.边):
    属性[边缘]=MH.边缘[边缘]
    属性[edge]['height']+=-np.min(随机高度)
    nx.设置节点属性(MH、属性)
    最短路径(MH,“A”,“G”,weight=“height”)
    打印(f“按高度排列的最佳路线(按最小负重量移动的重量):{Best_route_by_height_zero_negative}”)
    # 3. 获取每条路线的统计信息
    def get_路径_边缘_属性(
    G、 路由,属性=无,最小化\u key=“length”,检索\u默认值=无
    ):
    """
    """
    属性_值=[]
    对于压缩中的u,v(路径[:-1],路径[1:]):
    #如果两个节点之间存在平行边,请选择具有
    #最小化_键的最小值
    data=min(G.get\u edge\u data(u,v).values(),key=lambda x:x[最小化密钥])
    如果属性为“无”:
    属性值=数据
    elif retrieve_默认值不是无:
    属性\值=data.get(属性,检索\默认值(u,v))
    其他:
    属性值=数据[属性]
    属性值。追加(属性值)
    返回属性值
    stats_best_route={}
    统计数据\u最佳路径['by\u length']={'length':求和(获取路径\u边\u属性(MG,
    按长度排列的最佳路线,
    "长度"),,
    “高度”:求和(获取路径、边缘)属性(MG、,
    按长度排列的最佳路线,
    “高度”)}
    统计数据\u最佳路径['by\u height\u with \u negative']={'length':求和(获取\u route\u edge\u属性(MG,
    最佳路线按高度移动负,
    "长度"),,
    “高度”:求和(获取路径、边缘)属性(MG、,
    最佳路线按高度移动负,
    “高度”)}
    打印(f“统计最佳路由:{Stats\u best\u route}”)
    

    你能解释一下你认为什么是一个好的解决方案吗?如果有一个算法可以遵循的循环,那么您可以围绕该循环循环多次,并不断降低路径的权重。那么,在您的案例中,如何定义最小路径来避免这种情况呢?所以在你的第二个例子中,我可以选择一条循环C->E->F很多次的路径,它比你的路径小。嗨@Joel,谢谢你的评论。我应该提到,我正在寻找一个没有周期的解决方案。我会编辑我的问题。这可能会失败-考虑边A- B与重量1,然后交替路径A C-D B,第一边缘具有重量- 1/2,第二重量0,和第三有重量3/4。然后ACDB路径的总权重为1/4,小于A-B边。但是如果我们将所有内容移动1/2,那么ACDB路径的权重为1.75,而A-B边的权重为1.5。因此,由于边的数量会影响路径权重的偏移量,因此顺序会发生变化。
    Best route by height (weight shifted by minimum negative weight): ['A', 'C', 'E', 'G']
    Stats best routes: {'by_length': {'length': 13.7, 'height': 245.0}, 'by_height_with_negative': {'length': 24.8, 'height': -70.0}}