交通 | Python-MIP库的基础应用——以旅行商问题为例

2023-10-11 23:59

本文主要是介绍交通 | Python-MIP库的基础应用——以旅行商问题为例,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

推文作者:薛桂琴,大连海事大学,研究方向: 即时配送、交通优化

编者按:

本文介绍了python的MIP库的安装以及应用,以经典的旅行商问题为例介绍了MIP库求解的实现方式、对偶变量获取以及如何获得多个可行解。

0 引言

目前,python语言随着各种开源框架的应用而得到越来越多的使用,而运筹学的MIP库在python中扮演着重要的角色。本文介绍了MIP库的安装以及基础的使用,包括约束、变量的添加以及不等式应用等,并以经典的旅行商问题为例,介绍了python的MIP的使用情况。

1 工具包的安装

正常情况下,使用 pip install mip即可完成这个包的安装。

pip install mip

但是在我安装的使用报了下面这样一个错误:

ERROR: Could not install packages due to an EnvironmentError: [WinError 5] 拒绝访问。: ‘d:\…\anaconda3\lib\site-packages\_cffi_backend.cp38-win_amd64.pyd’

Consider using the --useroption or check the permissions.

如果你遇见了这个错误,可以使用在安装指令中添加 --user 选项,最终命令是:

pip install mip --user

2 基础API的使用

2.1 模型类 Model

这是mip库的主类,通过该类可以完成模型的构建、决策变量的添加、约束的添加、目标函数的设置、模型的求解以及解的状态查询。

在构造Model对象时,可以不传递任何参数,也可以指定相关参数:

# 方式1
mp = Model()
# 方式2:这种方式指定了模型的名称、模型的目标函数类型、求解模型要调用的求解器;这里指定为Gurobi,还可以使用开源的 CBC
mp = Model(name='TSP',sense=MINIMIZE,solver_name='GRB')
2.1.1 向模型中加入约束

添加约束涉及的api:

•add_constr(expr, name):包含约束表达式和约束的名称两个参数

•+= : 这是mip库特有的添加约束的方式

•如果你的约束中包含求和项,则需调用 xsum(expr) 方法,这个方法类似于gurobipy中的 quicksum() 方法

•add_cut() :向模型中添加有效不等式时,使用该方法

•add_lazy_constr():当初始模型不完整且发现整数解时,使用该方法添加规避掉当前整数解的约束

# 添加单个约束
model.add_constr(x1 + x2 <= 9, name='约束1')
model += x1 + x2 <= 9, "约束1"
# 添加求和约束
model += xsum(x[i] for i in range(2)) <= 9, '约束1'
2.1.2 向模型中添加变量

添加决策变量涉及的API:

•add_var(name=‘’, lb=0.0, ub=inf, obj=0.0, var_type=‘C’, column=None):决策变量的名称、下界、上界、目标系数、类型;Column在列生成时使用

•add_var_tensor(shape, name, **kwargs):一次性添加多个决策变量,shape属性表示决策变量的维度

x = model.add_var(name='x', lb=0.0, ub=10.0, var_type='C') # 创建一个连续型决策变量
x = model.add_var_tensor(name='x',shape=(3,3),var_type='B') # 创建3*3个二进制决策变量
x = [[ model.add_var(name='x',var_type='I') for i in range(0,3)] for j in range(0,3)] # 创建3*3个整数决策变量
2.1.3 模型状态检查

状态说明:

•ERROR = -1:求解器返回了一个错误

•OPTIMAL = 0:得到的最优解

•INFEASIBLE = 1:建立的模型是一个不可行模型

•UNBOUNDED = 2:目标函数值是无穷的,且存在一个或多个决策变量不受约束条件限制

•FEASIBLE = 3:在搜索过程中找到了整数解,但在证明他是最优解之前,求解被中断

•INT_INFEASIBLE = 4:问题的线性松弛存在可行解,但是不存在整数解

•NO_SOLUTION_FOUND = 5:执行了截断(truncated)搜索,没找到可行解

•LOADED = 6:问题被加载了,但是还有执行优化过程

•CUTOFF = 7:在当前cutoff下没有找到可行的解决方案

代码示例:

status = lp.optimize()
if status == OptimizationStatus.OPTIMAL:print('模型已求解到最优')
2.1.4 几个高效率的方法

下面介绍几个提升编码效率的有用的方法:

•xum():该方法用于构造带求和形式的线性表达式

•minimize():该方法用于构造最小化目标函数

•maximize():该方法用于构造最大化目标函数

•start():该方法可以为模型设置初始解

•model.threads:该属性可以配置求解模型用到的线程数目,默认使用求解器自身的线程配置;设置的线程多了可能会加速但是会增加内存消耗。

•model.read(filePath):从指定的路径下读取lp或mps模型文件

2.2 有效不等式的使用
2.2.1 生割或添加lazy_cut

要用的类是:ConstrsGenerator,该类的标准定义为:

class myConstrsGen:def _nint__():# 类的构造函数def generate_constrs(model, depth=0, npass=0):# 编写你的生割代码# 求解器引擎将自动调用该方法,因此这个方法是生割类必须有的方法,不能少
2.2.2 调用coin-or库中的有效不等式

这些有效不等式是定义在 CutType类中的,主要包含以下几种:

•PROBING = 0:Cuts generated evaluating the impact of fixing bounds for integer variables

•GOMORY = 1:Gomory Mixed Integer cuts, as implemented by John Forrest.

•GMI = 2:Gomory Mixed Integer cuts, as implemented by Giacomo Nannicini, focusing on numerically safer cuts.

•RED_SPLIT = 3:Reduce and split cuts [AGY05], implemented by Francois Margot.

•RED_SPLIT_G = 4:Reduce and split cuts [AGY05], implemented by Giacomo Nannicini.

•FLOW_COVER = 5:Lifted Simple Generalized Flow Cover Cut Generator.

•MIR = 6:Mixed-Integer Rounding cuts

•TWO_MIR = 7:Two-phase Mixed-integer rounding cuts.

•LATWO_MIR = 8:Lagrangean relaxation for two-phase Mixed-integer rounding cuts, as in LAGomory

•LIFT_AND_PROJECT = 9:Lift-and-project cuts [BCC93], implemented by Pierre Bonami.

•RESIDUAL_CAPACITY = 10:Residual capacity cuts [AtRa02], implemented by Francisco Barahona.

•ZERO_HALF = 11:Zero/Half cuts

•CLIQUE=12:Clique cuts

•ODD_WHEEL = 13:Lifted odd-hole inequalities.

•KNAPSACK_COVER = 14:Knapsack cover cuts

使用方法是:

 cp = model.generate_cuts([CutType.GOMORY, CutType.MIR, CutType.ZERO_HALF, CutType.KNAPSACK_COVER])
2.2.3 割池 CutPool

CutPool类中仅有一个方法, add();可以使用该方法将构造的一个或多个cut存储起来,最后统一添加到数学模型中。其典型的用法是:

cp = CutPool()
cp.add(x1 + x2 <= 12)
cp.add(x1 + x2 >= 5)
for cut in cp.cuts:model += cut
2.3 获取多个可行解

解释:

•num_solutions 属性,记录了可行解的个数

•objective_values 属性,记录了可行解的目标函数值

•xi 方法,记录了决策变量的值

for idx in range(multiSol.num_solutions):print(f'第{idx}个可行解的目标函数值为{multiSol.objective_values[idx]}')print(f"x1={x1.xi(idx)},x2={x2.xi(idx)}")

3 应用示例

3.1 旅行商问题
3.1.1 问题描述与模型定义

给定一组城市坐标,旅行商需要从其中的某一个城市出发,每个城市访问一次,最终返回到他出发的城市。

•集合定义:

  • N = 0 , 1 , 2 , . . . , n − 1 N={0,1,2,...,n-1} N=0,1,2,...,n1:标识城市列表索引
  • A = ( i , j ) , ∀ i ∈ N , j ∈ N A={(i,j),\forall i \in N, j\in N} A=(i,j),iN,jN:表示城市间的连接弧段集合

•参数:

  • c i j c_{ij} cij:表示城市 i i i到城市 j j j之间的距离

•决策变量:

  • x i j x_{ij} xij:二进制决策变量,标识城市 i i i到城市 j j j之间的弧段被旅行商使用时,取值为1;否则取值为0
  • u i u_{i} ui:整数决策变量,标识城市 i i i在旅行商路径中的序号;例如,城市 i i i是旅行商需要访问的第5个城市,则对应的决策变量取值为 u i = 4 u_{i}=4 ui=4,注意通常情况下,我们将起点序号标记为0。

目标函数是:最小化旅行商访问所有城市的总距离
m i n : ∑ i ∈ N ∑ j ∈ N x i j c i j min:\sum_{i\in N}\sum_{j\in N}x_{ij}c_{ij} min:iNjNxijcij

约束条件1:所有节点的出度为1
∑ j ∈ N x i j = 1 , ∀ i ∈ N \sum_{j \in N} x_{ij} = 1,\forall i \in N jNxij=1,iN

约束条件2:所有节点的入度为1
∑ i ∈ N x i j = 1 , ∀ j ∈ N \sum_{i \in N} x_{ij} = 1,\forall j \in N iNxij=1,jN

约束条件3:MTZ子回路消除约束
u i + 1 ≤ u j + ( n − 1 ) ∗ ( 1 − x i j ) , ∀ i , j ∈ N , i ≠ 0 u_{i} + 1 \leq u_{j} + (n-1)*(1-x_{ij}),\forall i,j \in N, i\neq 0 ui+1uj+(n1)(1xij),i,jN,i=0

注:MTZ形式的子回路消除约束中,编码时务必要注意 i 的取值不能为0,否则这个约束会在出发点处,产生矛盾;举个例子,假如我们有3个城市,分别记为0,1,2,得到的一条路径为 0-1-2-0;那么对应的 u 的取值为 u 0 = 0 u_{0}=0 u0=0
, u 1 = 1 u_{1}=1 u1=1, u 2 = 2 u_{2}=2 u2=2;由于2还要回到0,这就要求 u 0 = 3 u_{0}=3 u0=3,就跟前面的 u 0 = 0 u_{0}=0 u0=0向矛盾了。所以这里要对出发的城市做一个特殊处理。

约束条件4:决策变量的定义域约束
$$u_{i} \in [0,n-1],\forall i \in N \ x_{ij} \in {0,1},\forall i,j \in A$$

3.1.2 实现代码1

代码的编写思路:

•我们在[0, 200 ]之间随机生成节点的坐标,然后以节点间的欧式距离构造节点间的距离矩阵

•利用 mip 库的 Model 类创建tsp的模型

•利用 mip 库的 add_var_tensor() 方法构造决策变量矩阵

•利用 mip 库的 add_constr() 方法向模型中添加约束条件

•利用 mip 库的 xsum() 方法构造具有求和关系的约束表达式

•利用 mip 库的 write() 方法将建立的数学模型导出为 lp 格式的模型文件

•利用 mip 库的 optimize() 方法求解建立的数学模型

•最终我们使用 matplotlib.pyplot 将我们的路径打印出来

from mip import *
import numpy as np
import matplotlib.pyplot as plt
# 定义城市列表、城市坐标
num_of_city = 30
coors = np.random.randint(low=0, high=200, size=(num_of_city,2))
# 定义建模需要的节点集合和弧段集合
N = [i for i in range(0, num_of_city)]
A = [(i,j) for i in N for j in N if i!=j]
# 定义成本矩阵
cost = {(i,j):np.sqrt((coors[i,0]-coors[j,0])**2 + (coors[i,1]-coors[j,1])**2) for i,j in A}
# 构建模型
tsp = Model(name='TSP',sense=MINIMIZE,solver_name='GRB')
# 添加决策变量
x = tsp.add_var_tensor(shape=(num_of_city,num_of_city),var_type='B',name='x')
u = tsp.add_var_tensor(shape=(num_of_city,), name='u')
# 设置目标函数
tsp.objective = xsum(x[i,j]*cost[i,j] for i,j in A)
# 添加节点的入度约束
for j in N:tsp.add_constr(xsum(x[i,j] for i in N if i!=j) == 1)
# 添加节点的出度约束
for i in N:tsp.add_constr(xsum(x[i,j] for j in N if i!=j) == 1)
# 添加MTZ形式的子回路消除约束
for i,j in A:if i!=0:tsp.add_constr(u[i] + 1 <= u[j] + num_of_city*(1-x[i,j]))
tsp.optimize()
tsp.write('tsp.lp')
active_x = [(i,j) for i,j in A if x[i,j].x >= 0.99]
print(active_x)
for i,j in active_x:plt.plot([coors[i,0],coors[j,0]],[coors[i,1],coors[j,1]], c='c')
plt.show()
3.1.3 实现方式2

在上一个实现方式中,我们追求代码简洁,同时倾向于通过方法调用的方式构建模型;在这个实现这个实现方式中,我们中规中矩的使用官方推荐的方式来构建我们的TSP模型。

from mip import *
import numpy as np
import matplotlib.pyplot as plt
# 定义城市列表、城市坐标
num_of_city = 30
coors = np.random.randint(low=0, high=200, size=(num_of_city,2))
# 定义建模需要的节点集合和弧段集合
N = [i for i in range(0, num_of_city)]
A = [(i,j) for i in N for j in N if i!=j]
# 定义成本矩阵
cost = {(i,j):np.sqrt((coors[i,0]-coors[j,0])**2 + (coors[i,1]-coors[j,1])**2) for i,j in A}
# 构建模型
tsp = Model(name='TSP',sense=MINIMIZE,solver_name='GRB')
# 添加决策变量
x = [[tsp.add_var(var_type=BINARY) for i in N] for j in N]
u = [tsp.add_var(var_type='I') for i in N]
print(x)
# 设置目标函数
tsp.objective = xsum(x[i][j]*cost[i,j] for i,j in A if i!=j)
# 添加节点的入度约束
for j in N:tsp += xsum(x[i][j] for i in N if i!=j) == 1, f'indegree_{j}'
# 添加节点的出度约束
for i in N:tsp += xsum(x[i][j] for j in N if i!=j) == 1, f'oudegree_{i}'
# 添加MTZ形式的子回路消除约束
for i,j in A:if i!=0:tsp += u[i] + 1 <= u[j] + num_of_city*(1-x[i][j]),  f'sec_[{i},{j}]'
tsp.optimize()
tsp.write('tsp.lp')
active_x = [(i,j) for i,j in A if x[i][j].x >= 0.99]
print(active_x)
for i,j in active_x:plt.plot([coors[i,0],coors[j,0]],[coors[i,1],coors[j,1]], c='c')
plt.show()

两种实现方式的区别在于:

  • 在添加决策变量时,使用 mip 库的 add_var() 方法,以单个变量的形式添加到模型中的;由于列表定义的差异,在使用决策变量时,应该用 x [ i ] [ j ] x[i][j] x[i][j]而非第一种方式中的 x [ i , j ] x[i,j] x[i,j]

  • 在添加约束条件时,使用 mip 库提供的 += 操作符,而不是第一种方式的 add_constr() 方法

3.1.4 实现方式3

由于子回路消除约束是一类特殊的约束,可以在发现子回路时,再将它加入到模型中也是可以的。因此,我们可以首先求解TSP的线性松弛模型,然后根据松弛解构造图,并利用最小割算法生成子回路。最小割算法在networkX中提供有直接可用的方法 minimum_cut。

from mip import *
import numpy as np
import matplotlib.pyplot as plt
import networkx as nx# 定义城市列表、城市坐标
num_of_city = 30
coors = np.random.randint(low=0, high=200, size=(num_of_city,2))
# 定义建模需要的节点集合和弧段集合
N = [i for i in range(0, num_of_city)]
A = [(i,j) for i in N for j in N if i!=j]
# 定义成本矩阵
cost = {(i,j):np.sqrt((coors[i,0]-coors[j,0])**2 + (coors[i,1]-coors[j,1])**2) for i,j in A}
# 构建模型
tsp = Model(name='TSP',sense=MINIMIZE,solver_name='CBC')
# 添加决策变量
x = tsp.add_var_tensor(shape=(num_of_city,num_of_city),var_type='B',name='x')
u = tsp.add_var_tensor(shape=(num_of_city,), name='u')
# 设置目标函数
tsp.objective = xsum(x[i,j]*cost[i,j] for i,j in A)
# 添加节点的入度约束
for j in N:tsp.add_constr(xsum(x[i,j] for i in N if  i!=j) == 1)
# 添加节点的出度约束
for i in N:tsp.add_constr(xsum(x[i,j] for j in N if i!=j) == 1)newConstraints = Truewhile newConstraints:tsp.optimize(relax=True)print("status: {} objective value : {}".format(tsp.status, tsp.objective_value))G = nx.DiGraph()for a in A:G.add_edge(a[0], a[1], capacity=x[a].x)newConstraints = False# 使用最小割方法找到子回路时,将子回路加入到模型当中for (n1, n2) in [(i, j) for (i, j) in A if i != j]:cut_value, (S, NS) = nx.minimum_cut(G, n1, n2)if cut_value <= 0.99:tsp += (xsum(x[a] for a in A if (a[0] in S and a[1] in S)) <= len(S) - 1)newConstraints = True# 如果子回路消除约束不产生cut时,我们使用其他的割平面方法产生有效不等式if not newConstraints and tsp.solver_name.lower() == "cbc":cp = tsp.generate_cuts([CutType.GOMORY, CutType.MIR, CutType.ZERO_HALF, CutType.KNAPSACK_COVER])if cp.cuts:tsp += cpnewConstraints = Truetsp.write('tsp.lp')
active_x = [(i,j) for i,j in A if x[i,j].x >= 0.99]
print(active_x)
for i,j in active_x:plt.plot([coors[i,0],coors[j,0]],[coors[i,1],coors[j,1]], c='c')
plt.show()
3.1.5 实现方式4

最后我们讨论一种lazy cut的实现方式,在这种方式建立在初始模型是不完整的假设上的,也就是当模型捕获到一个整数解时,他会自动检查这个解是否时可行的;如果得到的整数解时不可行的,那么我们会添加一个或几个约束来确保未来这个不可行的整数解不会再出现。

from typing import List, Tuple
from random import seed, randint
from itertools import product
from math import sqrt
import networkx as nx
from mip import Model, xsum, BINARY, minimize, ConstrsGenerator, CutPool
import matplotlib.pyplot as pltclass SubTourCutGenerator(ConstrsGenerator):"""Class to generate cutting planes for the TSP"""def __init__(self, Fl: List[Tuple[int, int]], x_, N_):self.F, self.x, self.N = Fl, x_, N_def generate_constrs(self, model: Model, depth: int = 0, npass: int = 0):xf, N_, cp, G = tsp.translate(self.x), self.N, CutPool(), nx.DiGraph()for (u, v) in [(k, l) for (k, l) in product(N_, N_) if k != l and xf[k][l]]:G.add_edge(u, v, capacity=xf[u][v].x)for (u, v) in F:val, (S, NS) = nx.minimum_cut(G, u, v)if val <= 0.99:aInS = [(xf[i][j], xf[i][j].x) for (i, j) in product(N_, N_) if i != j and xf[i][j] and i in S and j in S]if sum(f for v, f in aInS) >= (len(S)-1)+1e-4:cut = xsum(1.0*v for v, fm in aInS) <= len(S)-1cp.add(cut)if len(cp.cuts) > 256:for cut in cp.cuts:model += cutreturnfor cut in cp.cuts:model += cut
if __name__ == '__main__':num_of_city = 50  # 城市数目N = set(range(num_of_city))seed(0)coors = [(randint(1, 100), randint(1, 100)) for i in N]  # coordinatesArcs = [(i, j) for (i, j) in product(N, N) if i != j]# 距离矩阵c = [[round(sqrt((coors[i][0]-coors[j][0])**2 + (coors[i][1]-coors[j][1])**2)) for j in N] for i in N]tsp = Model()# 二进制决策变量表示弧段是否被旅行商使用vx = [[tsp.add_var(var_type=BINARY) for j in N] for i in N]# 设置最小化距离目标函数tsp.objective = minimize(xsum(c[i][j]*x[i][j] for (i, j) in Arcs))# 出度一次约束for i in N:tsp += xsum(x[i][j] for j in N - {i}) == 1# 入度一次约束for i in N:tsp += xsum(x[j][i] for j in N - {i}) == 1# 移除仅包含两个点的子回路for (i, j) in Arcs:tsp += x[i][j] + x[j][i] <= 1# 对于点集中的每个点,我们找到距离其最远的点,然后首先检查他们是否构成了子回路F, G = [], nx.DiGraph()for (i, j) in Arcs:G.add_edge(i, j, weight=c[i][j])for i in N:P, D = nx.dijkstra_predecessor_and_distance(G, source=i)DS = list(D.items())DS.sort(key=lambda x: x[1])F.append((i, DS[-1][0]))# tsp.cuts_generator = SubTourCutGenerator(F, x, N)tsp.lazy_constrs_generator = SubTourCutGenerator(F, x, N)tsp.optimize()print('解的状态: %s 路径长度 %g' % (tsp.status, tsp.objective_value))tsp.write('tsp.lp')active_x = [(i,j) for i,j in Arcs if x[i][j].x >= 0.99]print(active_x)for i,j in active_x:plt.plot([coors[i][0],coors[j][0]],[coors[i][1],coors[j][1]], c='c')for i,txt in enumerate(N):plt.annotate(txt,(coors[i][0]+0.5, coors[i][1]+0.5))plt.show()
3.2获取线性规划问题的对偶变量
3.2.1 一个简单的线性规划问题

下面给出一个简单的线性规划问题,其对应的表达式为:

m a x : 2 x 1 + 3 x 2 − 5 x 3 max: 2x_{1} + 3x_{2} - 5x_{3} max:2x1+3x25x3
s . t s.t s.t
$$\x_{1} + x_{2} + x_{3} = 7 \ 2x_{1} - 5x_{2} + x_{3} \geq 10 \ x_{1} + 3x_{2} + x_{3} \leq 12 \ x_{1},x_{2},x_{3} \geq 0$$

求解盖模型的代码为:

from mip import *
lp = Model()
x = lp.add_var_tensor(shape=(3,),var_type='C',name='x')
lp.objective = maximize(2*x[0] + 3*x[1] - 5*x[2])
lp += x[0] + x[1] + x[2] == 7, '约束1'
lp += 2*x[0] - 5*x[1] + x[2] >= 10, '约束2'
lp += x[0] + 3*x[1] + x[2] <= 12, '约束3'
lp.optimize()
active_x = [x[i].x for i in range(3)]
print(active_x)

模型的最优解为:[6.428571428571429, 0.5714285714285712, 0.0],最优值为14.571

3.2.2 获取感兴趣的模型信息
  • 获取模型对应的对偶变量
active_pi = [lp.constr_by_name(f'约束{i}').pi for i in range(1,4)]
print(active_pi)

模型的对偶变量取值为:[2.2857142857142856, -0.14285714285714285, 0.0]

  • 获取模型的目标函数值
print(f'目标函数值为{lp.objective_value}')
print(f'目标函数值的界为{lp.objective_bound}')
3.3 获取优化模型的多个可行解​

3.3.1 简单的整数规划示例​

​下面给出一个简单的整数规划问题,其对应的表达式为:

m i n : 5 x 1 + 5 x 2 min: 5x1 + 5x2 min:5x1+5x2
s . t s.t s.t
x 1 + x 2 ≥ 6 x1 + x2 \geq 6 x1+x26
x 1 + x 2 ≥ 4 x1 + x2 \geq 4 x1+x24
x 1 + 3 x 2 ≥ 6 x1 + 3x2 \geq 6 x1+3x26
x 1 , x 2 ∈ Z + x1, x2 \in Z^{+} x1,x2Z+

求解盖模型的代码为:

3.3.2 获取多个可行解的验证代码
from mip import *
multiSol = Model()
x1 = multiSol.add_var(var_type='I',name='x1')
x2 = multiSol.add_var(var_type='I',name='x1')
multiSol.objective = 5*x1 + 5*x2
multiSol += 3*x1 + x2 >= 6
multiSol += x1 + x2 >= 4
multiSol += x1 + 3*x2 >= 6
status = multiSol.optimize(max_solutions=10)
if status == OptimizationStatus.OPTIMAL:print('模型已求解到最优')for idx in range(multiSol.num_solutions):print(f'第{idx}个可行解的目标函数值为{multiSol.objective_values[idx]}')print(f"x1={x1.xi(idx)},x2={x2.xi(idx)}")

打印结果:

模型已求解到最优
第0个可行解的目标函数值为20.0
x1=3.0,x2=1.0
第1个可行解的目标函数值为30.0
x1=0.0,x2=6.0

4.总结与展望

本文以旅行商问题为例,介绍了python的MIP库的应用,实现了在求解问题之外的多种应用,这对未来算法优化打下了坚实的基础。

参考文献:

https://buildmedia.readthedocs.org/media/pdf/python-mip/latest/python-mip.pdf

这篇关于交通 | Python-MIP库的基础应用——以旅行商问题为例的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/191775

相关文章

从基础到高级详解Go语言中错误处理的实践指南

《从基础到高级详解Go语言中错误处理的实践指南》Go语言采用了一种独特而明确的错误处理哲学,与其他主流编程语言形成鲜明对比,本文将为大家详细介绍Go语言中错误处理详细方法,希望对大家有所帮助... 目录1 Go 错误处理哲学与核心机制1.1 错误接口设计1.2 错误与异常的区别2 错误创建与检查2.1 基础

Python版本信息获取方法详解与实战

《Python版本信息获取方法详解与实战》在Python开发中,获取Python版本号是调试、兼容性检查和版本控制的重要基础操作,本文详细介绍了如何使用sys和platform模块获取Python的主... 目录1. python版本号获取基础2. 使用sys模块获取版本信息2.1 sys模块概述2.1.1

一文详解Python如何开发游戏

《一文详解Python如何开发游戏》Python是一种非常流行的编程语言,也可以用来开发游戏模组,:本文主要介绍Python如何开发游戏的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录一、python简介二、Python 开发 2D 游戏的优劣势优势缺点三、Python 开发 3D

Python函数作用域与闭包举例深度解析

《Python函数作用域与闭包举例深度解析》Python函数的作用域规则和闭包是编程中的关键概念,它们决定了变量的访问和生命周期,:本文主要介绍Python函数作用域与闭包的相关资料,文中通过代码... 目录1. 基础作用域访问示例1:访问全局变量示例2:访问外层函数变量2. 闭包基础示例3:简单闭包示例4

IDEA和GIT关于文件中LF和CRLF问题及解决

《IDEA和GIT关于文件中LF和CRLF问题及解决》文章总结:因IDEA默认使用CRLF换行符导致Shell脚本在Linux运行报错,需在编辑器和Git中统一为LF,通过调整Git的core.aut... 目录问题描述问题思考解决过程总结问题描述项目软件安装shell脚本上git仓库管理,但拉取后,上l

Python实现字典转字符串的五种方法

《Python实现字典转字符串的五种方法》本文介绍了在Python中如何将字典数据结构转换为字符串格式的多种方法,首先可以通过内置的str()函数进行简单转换;其次利用ison.dumps()函数能够... 目录1、使用json模块的dumps方法:2、使用str方法:3、使用循环和字符串拼接:4、使用字符

Python版本与package版本兼容性检查方法总结

《Python版本与package版本兼容性检查方法总结》:本文主要介绍Python版本与package版本兼容性检查方法的相关资料,文中提供四种检查方法,分别是pip查询、conda管理、PyP... 目录引言为什么会出现兼容性问题方法一:用 pip 官方命令查询可用版本方法二:conda 管理包环境方法

基于Python开发Windows自动更新控制工具

《基于Python开发Windows自动更新控制工具》在当今数字化时代,操作系统更新已成为计算机维护的重要组成部分,本文介绍一款基于Python和PyQt5的Windows自动更新控制工具,有需要的可... 目录设计原理与技术实现系统架构概述数学建模工具界面完整代码实现技术深度分析多层级控制理论服务层控制注

idea npm install很慢问题及解决(nodejs)

《ideanpminstall很慢问题及解决(nodejs)》npm安装速度慢可通过配置国内镜像源(如淘宝)、清理缓存及切换工具解决,建议设置全局镜像(npmconfigsetregistryht... 目录idea npm install很慢(nodejs)配置国内镜像源清理缓存总结idea npm in

pycharm跑python项目易出错的问题总结

《pycharm跑python项目易出错的问题总结》:本文主要介绍pycharm跑python项目易出错问题的相关资料,当你在PyCharm中运行Python程序时遇到报错,可以按照以下步骤进行排... 1. 一定不要在pycharm终端里面创建环境安装别人的项目子模块等,有可能出现的问题就是你不报错都安装