1. 全国大学生数学建模竞赛常用软件-优化求解器
2024年全国大学生数学建模竞赛开赛在即。其报名时间截止到2024年9月2日(周一)20时,竞赛时间确定为:2024年9月5日(周四)18时至2024年9月8日(周日)20时。参赛者可登陆官方网站全国大学生数学建模竞赛 进行登陆与信息查询。
在数学建模竞赛中,运筹学理论与方法显得尤为关键,广泛应用于解决跨交通、金融、制造、通信、排班调度、能源管理、国防策略等多领域的复杂决策优化问题。其中,在过去十年中有较多题目涉及到运筹学相关的优化方法,包括2017年CUMCM的D题“巡检线路的排班”、2020年CUMCM的C题“中小微企业的信贷决策”以及2023年CUMCM的C题“蔬菜类商品的自动定价和补货策略”&D题“圈养湖羊的空间利用率”等。一般来说,竞赛中常见的优化问题类型涉及线性规划,如生产计划、运输、投资组合优化等问题;以及整数规划,诸如求解最短路、旅行商、背包问题等。在限时竞赛环境下,准确快速求解上述优化问题更有可能在数学建模竞赛中取得好成绩。
为了简便高效的求解此类大规模优化问题,求解器应运而生。使用者只需编程将数学模型输入后调用求解器即可实现优化问题的快速求解。目前主流的优化求解器主要包括商用求解器与开源求解器两类,其中商用求解器具有效率卓越、求解稳定性较高等特点,而开源求解器具有代码开源灵活性较高、使用零成本等特点。此外还有一些商业软件如Matlab、Excel表格中含有求解工具,可求解部分规模较小的数学规划问题,但是求解效率和商用求解器存在一定差距,在处理复杂度高、规模大的问题时非常有可能出现无法求出最优解的情况。
综上所述,鉴于优化问题在数学建模竞赛中出现频率较高且其对竞赛成绩有显著影响,选择合适方式完成优化问题的求解至关重要,直接关乎模型求解的效率与效果,影响着最终的竞赛排名与解决方案的可行性。因此,深入了解并熟练掌握一类求解器的使用,对于每位参赛者而言,是竞赛准备“性价比”较高的一种方式,应在赛前得到充分重视与实践。
2. 案例分享-2017年全国大学生数学建模竞赛D题求解
在数模竞赛中,强力推荐使用达摩院研发的MindOpt求解器,其历经多次迭代升级,已发展成为一个功能完备、性能领先的自研求解器,支持线性规划、混合整数规划及非线性规划等主流问题求解。
在面对竞赛中常见的数学模型挑战时,MindOpt求解器展现出极高的求解效率。特别地,其可通过MindOpt APL语言方便地将LaTeX公式描述成程序对应修改,适合在模型研发期间使用。此外,其有丰富的优化问题案例库,仅需传入数据,运行已有代码即可完成旅行商等优化问题求解,案例库请点击下面链接获取,内含详细代码。
案例库链接:MindOpt_求解器_优化_智能决策_阿里云;
操作流程:点入链接后,点击所需问题类型旁的“复制项目”按钮后,单击“复制”,即可在页面上方的“概览”中找到对应项目,点击notebook按钮即可进入jupyterLab网页,将需要求解的数据按照格式代替已有案例数据,修改代码中参数并运行Jupyter代码即可完成求解。
比如,在案例库中有用MindOpt求解器解决背包问题、TSP问题等优化问题的详细思路和代码。
背包问题:组合优化问题:背包问题-MindOpt Studio;
TSP问题:旅行商问题:假期如何旅游省路费-MindOpt Studio;
此外,案例库中还有用python和MindOpt求解器解决2017年全国大学生数学建模竞赛D题等数学建模竞赛题目的详细思路和代码。
巡检线路的排班-2017全国大学生数学建模竞赛D题-MindOpt Studio
以下对该问题及其求解思路进行简述。
2.1. 问题描述
某化工厂内设有26个关键节点,这些节点的正常运行对于维持企业平稳生产至关重要。为确保对节点持续监控,需要制定一套巡检排班计划,要求工作人员按照预设的巡检周期对各节点逐一巡查。已知信息包括每个节点的巡检周期、完成单个节点巡检所需的时间、节点间的连通关系及节点间行走所需时间。目标是在遵循给定排班规则的前提下,设计出能够最大限度节省人力资源的巡检路线及排班方案,同时需要保证在排班方案中一定时间段内工作人员的工作时长尽可能均衡。其中,含有26个节点的连通图如下所示。
2.2. 求解思路
该巡检线路的排班问题可被总结为一类workforce scheduling and routing problems问题,需要决策的变量包括1)巡检人员数量;2)每位巡检员需巡检的节点;3)每位巡检员在班次内的巡检路线。最后,依据规划的巡检路线制定出巡检人员排班表。设计排班表的目标是在满足排班约束的情况下最小化巡检人员的使用数量。具体需要满足的排班约束包括:
1)巡检人员起点为节点22;
2)单个班次的工作时长为8小时左右;
3)任意节点需在对应的巡检周期内被访问并完成巡检;
4)任意节点完成一次巡检时需要一名巡检人员;
为保证巡检人员配置的最优,即实现人力资源使用数量最少的目标,需确保每位巡检员在满足巡检周期约束的情况下尽可能访问较多的节点,同时确保访问相同节点集合时巡检人员所选取路径为最短路。为了达到上述目标,我们采用的求解策略为:求解覆盖所有节点的最短巡检路径,并对该路径进行分割从而确定最优巡检人数。路径分割后会形成不同的巡检区域,每个巡检区域即对应一个巡检人员。如下图所示,对应虚线部分为遍历所有节点的最短路径,对2与3之间路径进行切割后,得到实线部分的最优巡检路径,巡检人员数量为2。
上述求解策略可分为三个阶段,其中在阶段1构建最短路模型,求解两两节点之间的最优行走路线,将给定的连通图数据补充为完全图,作为阶段2的输入。在阶段2构建TSP模型,求解覆盖所有节点的最短巡检路径,得到最短巡检路径中节点的遍历顺序。在阶段3构建弧分割模型,依据阶段2的节点访问顺序将节点分组,分组数量即为巡检人员数量,分组后节点访问顺序为对应巡检人员的巡检路线。
2.3. 模型展示
该问题被分解为三个子问题进行求解,分别是:最短路模型、TSP模型、弧分割模型,数学模型如下,详细的建模请参考案例链接。
最短路模型:
TSP模型:
弧分割模型:
2.4. MindOpt APL代码展示
对应上述三个模型的MindOpt APL代码如下所示,该建模语言与LaTex公式类似,可在建模过程中快速将公式转化为代码,详细的python API代码见案例链接所示。
最短路模型求解代码:
clear model;
# 以下展示通过MindOpt APL语法对模型进行展示
# 构建巡检地集合
set I = 1..26; #巡检地集合
# 读取数据:巡检地点的基本信息
# 读入:巡检地点序号,巡检周期,巡检时间
param : Name, Tour_time, Serve_time = read_csv("data/region_data.csv", skip=1, pattern="snn");
#读取数据:距离矩阵读入
param : Key : Dist = read_csv("output/complete_graph_data.csv", use_col="0,1,2", skip=1, pattern="n+");
# 起终点设置,目前设置起点为22,终点为1,可被替换
param start1 = 22;
param end1 = 1;
## 设置变量-------
var x[Key] binary;
## 预处理集合
set Ise = I without {start1, end1};
## 设置目标-------
# 目标(1),含义为最小化两点之间的路径长度
minimize TotalDistance:
sum{(i,j) in Key} Dist[i, j] * x[i, j];
## 设置约束-------
# 约束(2),流平衡约束
subto FluxBalanceCons:
forall {i in Ise}:
sum{j in I with (i, j) in Key} x[i,j] - sum{j in I with (j, i) in Key} x[j, i] == 0;
# 约束(3)与约束(4),起终点约束,可同时加入两个约束通过关键字and相连
subto StartEndCons:
sum{i in I with (start1, i) in Key} x[start1, i] == 1 and
sum{i in I with (i, end1) in Key} x[i, end1] == 1;
# 约束(5),表示双向弧不会被同时使用
subto ArcCons:
forall {(i, j) in Key}:
x[i, j] + x[j, i] <= 1;
solve;
display;
TSP模型求解代码:
clear model;
# 以下展示通过MindOpt APL语法对模型进行展示
set City = 1..26; # 读取城市序号
param CityName[City] = read "data/region_data.csv" as "1s" skip 1; #读取城市名称
param cityNum = card(City);
# 路程时间参数---
param time[City*City] = read "output/graph_data_new.csv" as "<1n,2n> 3n" skip 1; #读取travel cost
# 设定起始城市----
# 这里将初始城市设置为22
param startCity = 22; #选择对应数字序号的城市为起始城市(startCity),此处可根据需要修改成自己的出发城市
# 其他的城市
set City_internal = City - {startCity};
param endCity = cityNum + 1;
# 生成边的集合----
# 城市之间的两两互通(有方向) + 除了起始城市外的所有城市去结束城市
set Edge = {<i,j> in City*City with i!=j:<i,j>} + {k in City_internal:<k, endCity>};
#print Edge;
## 定义变量-----------
var u[City] >= 0 ; # MTZ模型需要,代表各个城市是路径中的第几个点
var x[Edge] binary; #这个边是否有
## 目标函数------------
minimize totalCost :
sum{<i,j> in Edge with j != endCity} time[i,j] * x[i,j] +
sum{k in City_internal} time[k,startCity]*x[k,endCity];
# 注:结束节点endCity的路费,用startCity的price来替
## 约束函数------------
# 约束1:每个城市离开1次,除结束城市外
subto leaveCityEquals1_:
forall {c in City}
sum{ <c,j> in Edge} x[c,j] == 1;
# 约束2:每个城市进入1次,除起始城市外
subto enterCityEquals1_:
forall {c in (City_internal + {endCity})}
sum{ <i,c> in Edge} x[i,c] == 1;
# 约束3:消除子环
subto MTZ_:
forall {<i,j> in Edge with j!=endCity }
u[i] - u[j] + cityNum * x[i,j] <= cityNum-1;
solve;
display;
弧分割模型求解代码:
clear model;
# 以下展示通过MindOpt APL语法对模型进行展示
set I = 0..27; #间隙集合
#######################################
# 该块内的参数(param)均需要读取数据先行初始化
#######################################
# 读取数据:节点的基本信息
# 读取数据:距离矩阵读入
param : Key : Dist, Tour = read_csv("output/pre_route_for_mini.csv", use_col="0,1,4,5", skip=1, pattern="n+");
param start1 = 0;
param end1 = 27;
## 设置变量-------
var x[Key] binary;
## 预处理集合
set Ise = I - {start1, end1};
set Is = I - {start1};
set Ie = I - {end1};
## 设置目标-------
# 目标函数式(1),表示最小化两点之间的路径长度
minimize TotalDistance:
sum{(i,j) in Key} x[i, j];
## 设置约束-------
# 约束(2),表示流平衡约束
subto FluxBalanceCons:
forall {i in Ise}:
sum{j in I with (i, j) in Key} x[i,j] - sum{j in I with (j, i) in Key} x[j, i] == 0;
# 约束(3)与约束(4),分别表示起终点约束
subto StartEndCons:
sum{i in Is} x[start1, i] == 1 and
sum{i in Ie} x[i, end1] == 1;
# 约束(5),表示双向弧不会被同时使用
subto CapCons:
forall {(i, j) in Key}:
Dist[i, j] * x[i, j] <= Tour[i, j];
solve;
display;
附案例库中求解2017年全国大学生数学建模竞赛D题的详细思路和代码: