python调用求解器SCIP求解最短路径问题(Shortest Path Problem)

1. 数学模型

m i n ∑ ( i , j ) ∈ A c i j x i j ∑ j ∈ V x i j − ∑ j ∈ V x j i = { − 1 , i = s 0 , i ≠ s a n d i ≠ t 1 , i = t min\sum_{(i,j)\in A}{c_{ij} x_{ij}}\\ \sum_{j \in V}x_{ij} - \sum_{j\in V}x_{ji} = \begin{cases} -1, & {i = s } \\ 0, & {i \neq s \quad and \quad i \neq t}\\ 1, & {i=t}\\ \end{cases} min(i,j)AcijxijjVxijjVxji= 1,0,1,i=si=sandi=ti=t
x i j x_{ij} xij: 0-1决策变量, x i j = 1 x_{ij}=1 xij=1表示经过弧 ( i , j ) (i, j) (i,j), x i j = 0 x_{ij}=0 xij=0表示不经过弧 ( i , j ) (i, j) (i,j)

c i j c_{ij} cij: 弧 ( i , j ) (i, j) (i,j)上的权重

A A A: 所有弧的集合

V V V: 所有点的集合

s , t s, t s,t :分别表示源节点和结束节点

2. python调用SCIP求解最短路径问题

  • 根据上面的数学模型建立规划模型,完整代码如下:
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx
import time
import pyscipopt as opt


def print_fun_run_time(func):
    def wrapper(*args, **kwargs):
        local_time = time.time()
        func(*args, **kwargs)
        print('current function [%s] run time is %.4f'%(func.__name__, time.time()-local_time))
    return wrapper


def prepare_data():
    # 测试数据: 邻接矩阵标识图结构,其中极大值1000表示不连通, 测试数据包含11个节点
    matrix = np.array([[0, 2, 8, 1, 1000, 1000, 1000, 1000, 1000, 1000, 1000],
                       [2, 0, 6, 1000, 1, 1000, 1000, 1000, 1000, 1000, 1000],
                       [8, 6, 0, 7, 5, 1, 2, 1000, 1000, 1000, 1000],
                       [1, 1000, 7, 0, 1000, 1000, 9, 1000, 1000, 1000, 1000],
                       [1000, 1, 5, 1000, 0, 3, 1000, 2, 1000, 1000, 1000],
                       [1000, 1000, 1, 1000, 3, 0, 4, 1000, 6, 1000, 1000],
                       [1000, 1000, 2, 9, 1000, 4, 0, 1000, 3, 1, 1000],
                       [1000, 1000, 1000, 1000, 2, 1000, 1000, 0, 7, 1000, 9],
                       [1000, 1000, 1000, 1000, 1000, 6, 3, 7, 0, 1, 2],
                       [1000, 1000, 1000, 1000, 1000, 1000, 1, 1000, 1, 0, 4],
                       [1000, 1000, 1000, 1000, 1000, 1000, 1000, 9, 2, 4, 0]])

    # 转换数据(只是为了方便理解,不为效率), 获取不包含本节点且连通的点及弧
    row, col = np.where((matrix != 1000) & (matrix != 0))
    Nodes = list(np.unique(np.unique(np.append(row, col)) + 1).astype('str'))
    Arcs = {}
    for i in range(len(row)):
        Arcs[str(row[i] + 1), str(col[i] + 1)] = matrix[row[i], col[i]]
    print('Nodes:\n', Nodes)
    print('Arcs:\n', Arcs)
    return Nodes, Arcs


@print_fun_run_time
def SCIP_solver(Nodes, Arcs, source, target):
    model = opt.Model('SPP')

    # ==========定义变量==========
    arc = {}
    for idx in Arcs.keys():
        arc[idx] = model.addVar(vtype='B', name=str(idx))

    # ==========定义约束==========
    # 其实类似VRP的流量约束,源节点一定是流出,结束节点一定是流入,其他节点有流入一定有流出

    # 约束1: 源点一定流出
    model.addCons(opt.quicksum(arc[idx] for idx in Arcs.keys() if idx[0] == source) == 1, name='source_flow')
    # 约束2: 终点一定流入
    model.addCons(opt.quicksum(arc[idx] for idx in Arcs.keys() if idx[1] == target) == 1, name='target_flow')
    # 约束3: 其他点流平衡
    for node in Nodes:
        if node != source and node != target:
            model.addCons(opt.quicksum(arc[idx] for idx in Arcs.keys() if idx[0] == node and idx[1] != source) -
                          opt.quicksum(arc[idx] for idx in Arcs.keys() if idx[1] == node and idx[0] != target) == 0)

    # ==========定义目标==========
    model.setObjective(opt.quicksum(arc[arc_idx] * arc_weight for arc_idx, arc_weight in Arcs.items()))

    model.setMinimize()
    model.optimize()

    # ==========输出结果==========
    distance = model.getObjVal()

    # 转换结果
    visit_arcs = []
    for idx in Arcs.keys():
        if model.getVal(arc[idx]) > 0:
            visit_arcs.append(idx)
    visit_arcs_dict = dict([list(i) for i in visit_arcs])
    idx = source
    path = []
    for i in range(len(Nodes)):
        next_node = visit_arcs_dict.get(idx, None)
        if next_node is not None:
            path.append(next_node)
            idx = next_node

    print(f'节点 {source}{target} 的路径={path}, 最短距离={distance}')
    return True


if __name__ == '__main__':
    source = '1'  # starting node
    target = '11'  # ending node
    # 测试数据
    Nodes, Arcs = prepare_data()
    # 方式3: python调用开源求解器SCIP求解最短路径问题
    SCIP_solver(Nodes, Arcs, source, target)

3. 算法结果

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值