使用Gurobi筛选25节点子图以最大化指定节点间边连通性的代码问题咨询
Gurobi筛选25节点子图以最大化指定节点间边连通性的代码问题咨询
首先得戳破你遇到的核心矛盾:你想把Gurobi的决策变量矩阵E直接丢给NetworkX的连通性函数当目标,但E是Gurobi的MVar类型(不是固定的Numpy数组),local_edge_connectivity根本没法处理它——更关键的是,Gurobi的目标函数必须是决策变量的显式数学表达式,不能是一个需要动态生成图结构的黑箱函数,这才是问题的根源。
那怎么绕开这个坑?我们可以用Menger定理来转译问题:两个节点间的边连通性,等于它们之间最多的边不相交路径数。所以我们可以把“最大化A和B的边连通性”,转换成“最大化k,使得存在k条从A到B的不相交路径,同时子图恰好包含25个节点(必须包含A、B)”。
下面是修正后的完整建模思路和代码:
建模思路拆解
- 保留节点选择变量
F:F[node]为1表示选中该节点,0则不选; - 新增边选择变量
e:e[u,v]为1表示保留边(u,v),只有当u和v都被选中时,这条边才能保留; - 新增流变量
f:f[u,v]表示从u到v的流量,用来模拟k条不相交路径; - 约束条件:
- 选中节点数恰好25,且A、B必须被选中;
- 边只能在两端节点都被选中时存在;
- 流守恒:除A、B外,其他节点流入等于流出;A的总流出是k,B的总流入是k;
- 每条边的流量不超过边是否被选中的变量(因为每条边最多走1条路径)。
修正后的代码实现
import networkx as nx import gurobipy as grb # 初始化原图 G = nx.hoffman_singleton_graph() A = 5 B = 20 thresh_nodes = 25 # 整理节点和边的索引映射 nodes = list(G.nodes) edges = list(G.edges) # 初始化Gurobi模型 model = grb.Model("max_connectivity_subgraph") # 1. 节点选择变量:F[node] = 1 表示选中该节点 F = model.addVars(nodes, vtype=grb.GRB.BINARY, name="node_select") # 2. 边选择变量:e[u,v] = 1 表示保留该边 e = model.addVars(edges, vtype=grb.GRB.BINARY, name="edge_select") # 3. 流变量:f[u,v] 表示边(u,v)上的路径流量 f = model.addVars(edges, vtype=grb.GRB.CONTINUOUS, name="flow") # 4. k 是我们要最大化的边连通性(即最大不相交路径数) k = model.addVar(vtype=grb.GRB.INTEGER, name="max_connectivity") # --- 核心约束 --- # 选中节点数恰好为25 model.addConstr(grb.quicksum(F[node] for node in nodes) == thresh_nodes, name="node_count_limit") # 强制选中A和B model.addConstr(F[A] == 1, name="must_keep_A") model.addConstr(F[B] == 1, name="must_keep_B") # 边只能在两端节点都被选中时保留 for u, v in edges: model.addConstr(e[u, v] <= F[u], name=f"edge_{u}_{v}_depends_on_u") model.addConstr(e[u, v] <= F[v], name=f"edge_{u}_{v}_depends_on_v") # 流守恒约束:除A、B外,其他节点流入等于流出 for node in nodes: if node == A or node == B: continue inflow = grb.quicksum(f[v, node] for v in G.neighbors(node) if (v, node) in edges) outflow = grb.quicksum(f[node, v] for v in G.neighbors(node) if (node, v) in edges) model.addConstr(inflow == outflow, name=f"flow_conserv_{node}") # A的总流出量等于k,B的总流入量等于k model.addConstr(grb.quicksum(f[A, v] for v in G.neighbors(A)) == k, name="source_flow") model.addConstr(grb.quicksum(f[v, B] for v in G.neighbors(B)) == k, name="sink_flow") # 每条边的流量不能超过边是否被选中的变量(每条边最多走1条路径) for u, v in edges: model.addConstr(f[u, v] <= e[u, v], name=f"flow_cap_{u}_{v}") # 目标:最大化边连通性k model.setObjective(k, grb.GRB.MAXIMIZE) # 执行优化 model.optimize() # --- 提取并验证结果 --- if model.status == grb.GRB.OPTIMAL: # 筛选选中的节点和边 selected_nodes = [node for node in nodes if F[node].X > 0.5] selected_edges = [(u, v) for u, v in edges if e[u, v].X > 0.5] # 构建子图 subG = nx.Graph() subG.add_nodes_from(selected_nodes) subG.add_edges_from(selected_edges) # 验证连通性 actual_connectivity = nx.local_edge_connectivity(subG, A, B) print(f"计算得到的最大边连通性: {int(k.X)}") print(f"实际验证的边连通性: {actual_connectivity}") print(f"选中的节点数量: {len(selected_nodes)}") else: print("未找到可行的最优解")
为什么你的原代码跑不通?
你原代码里试图用local_edge_connectivity当目标函数,但Gurobi根本没法理解这个函数的内部逻辑——它需要的是能直接用决策变量拼出来的数学式子,而不是一个需要先构建图再计算的黑箱。通过把连通性转译成流的模型,我们把问题变成了Gurobi能处理的混合整数线性规划问题。
备注:内容来源于stack exchange,提问作者HJA24
相关产品推荐
相关产品推荐

