可怕的!数学建模——2019F题详细过程解

19 篇文章 9 订阅
11 篇文章 17 订阅

1 数学模型

决策变量:

x i j x_{ij} xij代表在或不在最短轨迹上;非负变量 v i v_{i} vi h i h_{i} hi 代表误差 。

x i j = { 1 ( i , j ) ∈ P A B 0 ( i , j ) ∉ P A B ; I i = { 1 i ∈ V 0 i ∈ H ; { v i ≥ 0 h i ≥ 0 x_{ij}=\left\{ \begin{aligned} 1 && (i,j)\in P_{AB} \\ 0 && (i,j)\notin P_{AB} \end{aligned} \right. ; I_{i}=\left\{ \begin{aligned} 1 && i\in V \\ 0 && i\in H \end{aligned} \right. ; \left\{ \begin{aligned} v_i &\geq& 0 \\ h_i &\geq& 0 \end{aligned} \right. xij={10(i,j)PAB(i,j)/PAB;Ii={10iViH;{vihi00

约束条件:

1) v A = 0 , h A = 0 , v B ≤ θ , h B ≤ θ v_{A}=0, h_{A}=0, v_{B} \leq \theta, h_{B} \leq \theta vA=0,hA=0,vBθ,hBθ

v i ≤ ( 1 − I i ) × 20 + I i × 25 h i ≤ ( 1 − I i ) × 25 + I i × 15 \begin{array}{ll} v_{i} \leq & \left(1-I_{i}\right) \times 20+I_{i} \times 25 \\ h_{i} \leq & \left(1-I_{i}\right) \times 25+I_{i} \times 15 \end{array} vihi(1Ii)×20+Ii×25(1Ii)×25+Ii×15

I i I_i Ii=1, v i ≤ 25 v_i \le 25 vi25 h i ≤ 15 h_i \le 15 hi15,垂直误差校正前需要满足的条件; I i I_i Ii=0, v i ≤ 20 v_i \le 20 vi20 h i ≤ 25 h_i \le 25 hi25,水平误差校正前需要满足的条件。

2)限制最短路径上前一点和后一点的关系式满足 I i h i + δ d i j = h j 、 ( 1 − I i ) v i + δ d i j = v j I_{i} h_{i}+\delta d_{i j}=h_{j}、(1-I_{i}) v_{i}+\delta d_{i j}=v_{j} Iihi+δdij=hj(1Ii)vi+δdij=vj

I i h i + δ d i j − h j ≤ ( 1 − x i j ) M ( 1 − I i ) v i + δ d i j − v j ≤ ( 1 − x i j ) M \begin{array}{l} I_{i} h_{i}+\delta d_{i j}-h_{j} \leq\left(1-x_{i j}\right) M \\ \left(1-I_{i}\right) v_{i}+\delta d_{i j}-v_{j} \leq\left(1-x_{i j}\right) M \end{array} Iihi+δdijhj(1xij)M(1Ii)vi+δdijvj(1xij)M

d i s t = ( d i j ) dist=(d_{ij}) dist=(dij) d i j d_{ij} dij对应 i i i点到 j j j点的飞行距离;M表示一个非常大的数,用来增加或释放约束, x i j = 0 x_{ij}=0 xij=0,表示释放上式, x i j = 1 x_{ij}=1 xij=1,表示约束上式,右边为0。

3)限制同类顶点之间的有向边距离小于等于10 km

d i j ≤ 10000 , i ∈ V , j ∈ V d i j ≤ 10000 , i ∈ H , j ∈ H \begin{array}{l} d_{ij}\leq 10000,i\in V,j \in V \\ d_{ij}\le 10000,i\in H,j \in H \end{array} dij10000iV,jVdij10000iH,jH

4)在最短路径上,限制A点为起点,那么只一出不进;限制B点为终点,那么只一进不出;其他点为一进一出。

{ ∑ j = 0 613 x i j = 1 , i = 0 ( A 点 ) , ∑ i = 0 613 x i j = 0 , j = 0 ( A 点 ) ∑ i = 0 613 x i j = 1 , j = 613 ( B 点 ) , ∑ j = 0 613 x i j = 0 , i = 613 ( B 点 ) ∑ i = 0 613 ∑ j = 1 612 x i j = ∑ j = 1 612 ∑ k = 0 613 x j k \left\{ \begin{aligned} \sum_{j=0}^{613}x_{ij} =1 ,i=0(A点),\sum_{i=0}^{613}x_{ij} =0 ,j=0(A点)\\ \sum_{i=0}^{613}x_{ij} =1 ,j=613(B点),\sum_{j=0}^{613}x_{ij} =0 ,i=613(B点)\\ \sum_{i=0}^{613}\sum_{j=1}^{612}x_{ij} =\sum_{j=1}^{612}\sum_{k=0}^{613}x_{jk} \end{aligned} \right. j=0613xij=1,i=0Ai=0613xij=0,j=0Ai=0613xij=1,j=613Bj=0613xij=0,i=613Bi=0613j=1612xij=j=1612k=0613xjk

目标函数:

m i n z 1 = ∑ i = 0 613 ∑ j = 0 613 d i j x i j min \quad z_1=\sum_{i=0}^{613}\sum_{j=0}^{613}d_{ij}x_{ij} minz1=i=0613j=0613dijxij

m i n z 2 = ∑ i = 0 613 ∑ j = 0 613 x i j min \quad z_2=\sum_{i=0}^{613}\sum_{j=0}^{613}x_{ij} minz2=i=0613j=0613xij

可以发现 z 1 z_1 z1 z 2 z_2 z2相差一个 d i j d_{ij} dij,为综合考虑两个目标,将两个目标放在几乎同一个数量级上,并对两个目标函数进行加权组合,权值各为0.5
m i n 0.5 z 1 + 0.5 ( m a x ( d i j ) ) z 2 min \quad 0.5z_1+0.5(max(d_{ij}))z_2 min0.5z1+0.5(max(dij))z2

2 Python代码—利用Python进行数据筛选、模型建立及求解全过程

1) 之前的工作—生成dist矩阵

#======================生成dist矩阵======================
# 导入库
import pandas as pd
import numpy as np

# 数据读取
io = r'数据.xlsx' #io,Excel的存储路径
sheet_name=0 #sheet_name,要读取的工作表名称:可以是整型数字、列表名或SheetN,也可以是上述三种组成的列表
usecols=range(0,5) #需要读取哪些列
skiprows=1 #跳过前n行;skiprows = [a, b, c],跳过第a+1,b+1,c+1行(索引从0开始)

data = pd.read_excel(io=io, sheet_name=sheet_name, usecols=usecols, skiprows=skiprows)
print(data.head())#展示部分结果

#校正列
I=data.iloc[:,4].values

# 编号列
N=list(data.iloc[:,0].values)

# 确定校正点坐标
V=[];#垂直校正点
H=[];#水平校正点

for i in N:
    if data.iloc[i,4]==1:
        V.append(N[i]);#垂直校正点        
    if data.iloc[i,4]==0:       
        H.append(N[i]);#水平校正点
        
# data删掉编号列
data=data.iloc[:,1:5]
print('data:\n',data)
print('----------------------------------------------------')
print('\ndata的数据类型',type(data))

# 定义dist初始矩阵
dist=np.zeros((613,613))

# 定义欧式距离函数
def Ed_Dis(m, n):
    return np.linalg.norm(m - n)

# 计算距离并存入dist
for h in range(613):
    m=data.iloc[h,:3].values    
    for l in range(613):
        n=data.iloc[l,:3].values
        
        dist[h,l]=Ed_Dis(m, n)

2) 进行边权值的筛选

#======================将dist中不同点,即不为0的权值以编号的形式存储======================
#这里注意,只对编号列表进行删除,保留dist,可以避免例如元素数据为0等造成不必要的麻烦(毕竟普通的矩阵计算对于计算机来说非常简单)
Boundary=[]
for i in range(613):
    for j in range(613):
        if dist[i,j]!=0:
            Boundary.append([i,j])
print('\n初始权值数:',len(Boundary))
#======================(1)约束:删掉以AB为轴,半径10000外的点======================
#定义A→B向量x
A=data.iloc[0,:3].values
B=data.iloc[-1,:3].values
x_AB=B-A

cut_dian1=[]
for i in range(1,612):
    y_i=data.iloc[i,:3].values
    iB=B-y_i
    if dist[i, 612]!=0:
        
        if dist[i, 612] * np.sqrt(1 - ((np.dot(iB,x_AB)) / (dist[0, 612] * dist[i, 612])) ** 2) >= 10000:
            cut_dian1.append(i)
#————————————————————————————————————————————————————————————————————————————————————
#实现删除
import copy
Boundary1 = copy.copy(Boundary)
#print(len(Boundary1))

for [i, j] in Boundary1:    
    if i in cut_dian1 or j in cut_dian1:
        Boundary.remove([i, j])
print("删掉以AB为轴,半径10000外的点之后:", len(Boundary))
#======================(2)约束:、利用校正条件筛选======================

cut_dian2=[]

for i in range(1,612):
    # 垂直误差不大于25个单位,水平误差不大于15个单位时才能进行垂直误差校正
    for j in V:
        if (0.001*dist[i,j])<=15:
            continue # 跳出本次循环,进入下一次循环
        else:
            cut_dian2.append([i,j]) # 不满足条件不可进行垂直误差调整
    
    # 垂直误差不大于20个单位,水平误差不大于25个单位时才能进行水平误差校正
    for j in H:
        if (0.001*dist[i,j])<=20:
            continue # 跳出本次循环,进入下一次循环
        else:
            cut_dian2.append([i,j]) # 不满足条件不可进行水平误差调整
    
    # 到达终点时垂直误差和水平误差均应小于30个单位
    for j in [612]:
        if (0.001*dist[i,j])<30:
            continue # 跳出本次循环,进入下一次循环
        else:
            cut_dian2.append([i,j]) # 不满足条件不能按照规划路径到达B点
#————————————————————————————————————————————————————————————————————————————————————
#实现删除
import copy
Boundary2 = copy.copy(Boundary) 
#print(len(Boundary2))

for [i, j] in Boundary2:
    if [i,j] in cut_dian2:
        
        Boundary.remove([i, j])
print("校正条件筛选之后:", len(Boundary))
#======================(3)约束:删去距离大于 10 km 的同类顶点之间的有向边======================

cut_dian3=[]

# 删去距离大于 10 km 的同为垂直校正顶点之间的有向边;
for i in V:    
    for j in V:        
        if dist[i,j]>10000:
            cut_dian3.append([i,j])
        else:            
            continue # 跳出本次循环,进入下一次循环

# 删去距离大于 10 km 的同为水平校正顶点之间的有向边;
for i in H:
    for j in H:        
        if dist[i,j]>10000:
            cut_dian3.append([i,j])
        else:            
            continue # 跳出本次循环,进入下一次循环

#————————————————————————————————————————————————————————————————————————————————————
#实现删除
import copy
Boundary3 = copy.copy(Boundary)  # 复制一个
#print(len(Boundary3))

for [i, j] in Boundary3:
    if [i,j] in cut_dian3:
        
        Boundary.remove([i, j])
print("删去距离大于 10 km 的同类顶点之间的有向边之后:", len(Boundary))
#======(4)约束:删除方向与前进方向相反的有向边(标准:在这里定义与A→B向量与任意向量夹角大于90°认为与前进方向相反)======

# 定义计算夹角函数 cos值在[0,1]之间,代表角度在0~90°;cos值在[-1,0]之间,代表角度在90°~180°
# 所以这里只需要计算cos值即可判断角度的范围
def Angle_cos(x, y):
    
    # 分别计算两个向量的模:
    l_x=np.sqrt(x.dot(x))
    l_y=np.sqrt(y.dot(y))
    #print('向量的模=',l_x,l_y)
    
    # 计算两个向量的点积
    dian=x.dot(y)
    #print('向量的点积=',dian)
    
    # 计算夹角的cos值:
    cos_=dian/(l_x*l_y)
    #print('夹角的cos值=',cos_)
    
    return cos_
#————————————————————————————————————————————————————————————————————————————————————
#定义A→B向量x
A=data.iloc[0,:3].values
B=data.iloc[-1,:3].values
x_AB=B-A

cut_dian4=[]
for i in range(613):
    y_i=data.iloc[i,:3].values
    
    for j in range(613):
        y_j=data.iloc[j,:3].values
        
        if i!=j:            
            # i→j向量y
            y_ij=y_j-y_i
            
            Ang_cos=Angle_cos(x_AB, y_ij)  # cos值在[0,1]之间,代表角度在0~90°;cos值在[-1,0]之间,代表角度在90°~180°
            if Ang_cos<=0:
                cut_dian4.append([i,j])
            else:
                continue # 跳出本次循环,进入下一次循环
#————————————————————————————————————————————————————————————————————————————————————
#实现删除
import copy
Boundary4 = copy.copy(Boundary)  # 复制一个
#print(len(Boundary4))

for [i, j] in Boundary4:
    if [i,j] in cut_dian4:
        
        Boundary.remove([i, j])
print("删除方向与前进方向相反的有向边之后:", len(Boundary))

3) 模型计算前需要进行的一些准备工作

Boundary_ab = copy.copy(Boundary)  # 复制一下,以防后面对Boundary还有什么改变方便操作

I[0]=0 # 由于I中起点和终点信息为字符无法进行运算,故在这里将起点的字符改变为0,从后面的约束条件可以发现对结果没有影响,而终点不会用到,故不用更改
#————————————————————————————————————————————————————————————————————————————————————
#根据Gurobi数据类型的要求对dist进行格式更换
# 将dist转换为tupledict类型
from gurobipy import *
dist_tup = {}
for i in range(613):
    for j in range(613):
        dist_tup[i,j] = dist[i,j]
print(type(dist_tup))
dist_tup = tupledict(dist_tup)
print(type(dist_tup))
#————————————————————————————————————————————————————————————————————————————————————
#寻找dij的最大值
max_dij=np.max(dist)
print('max_dij=',max_dij)

max_d = {}
for i in range(613):
    for j in range(613):
        max_d[i,j] = max_dij
print(type(max_d))
max_d = tupledict(max_d)
print(type(max_d))

4) 建立模型并求解

from gurobipy import *

# Create a new model
m = Model("feiji")

# Create variables vtype(可选):新变量的数据类型(GRB.CONTINUOUS,GRB.BINARY,GRB.INTEGER,GRB.SEMICONT,GRB.SEMIINT)
x = m.addVars(613, 613, vtype=gurobipy.GRB.BINARY, name="x")
v = m.addVars(613, vtype=gurobipy.GRB.CONTINUOUS, name="v")
h = m.addVars(613, vtype=gurobipy.GRB.CONTINUOUS, name="h")

# 更新变量环境
m.update()

# Set objective
m.setObjective(0.5*dist_tup.prod(x) + 0.5*max_d.prod(x), GRB.MINIMIZE)

# Add constraint1:
for i in range(613):
    if i == 0:
        # 起点的垂直和水平误差为0
        m.addConstr(h[i] == 0)
        m.addConstr(v[i] == 0)
    elif 0 < i < 612:
        # 中间点的误差约束条件
        m.addConstr(v[i]<=20*(1-I[i])+25*I[i])
        m.addConstr(h[i]<=25*(1-I[i])+15*I[i])
    else:
        # 终点前的垂直和水平误差约束条件
        m.addConstr(h[i] <= 30)
        m.addConstr(v[i] <= 30)

## Add constraint2:
M = 10000
for (i, j) in Boundary_ab:

    m.addConstr(I[i] * h[i] + 0.001 * dist[i, j] - h[j] <= M * (1 - x[i, j]))
    m.addConstr((1 - I[i]) * v[i] + 0.001 * dist[i, j] - v[j] <= M * (1 - x[i, j]))
            
#Add constraint3: 在最短路径上,限制A点为起点,那么只出不进;限制B点为终点,那么只进不出;其他点为一进一出
out_degree = [0] * 613
in_degree = [0] * 613
# out_degree[i]表示第i个点的出度

for (i, j) in Boundary_ab:
    out_degree[i] = out_degree[i] + x[i, j]
# in_degree[i]表示第i个点的入度
for (j, i) in Boundary_ab:
    in_degree[i] = in_degree[i] + x[j, i]

for i in range(613):
    if i == 0:
        # 起点A的出度为1,入度为0
        m.addConstr(out_degree[i] == 1)
        m.addConstr(in_degree[i] == 0)
    elif 0 < i < 612:
        # 中间点的出度等于入度
        m.addConstr(out_degree[i] == in_degree[i])
    else:
        # 终点B的入度为1,出度为0
        m.addConstr(out_degree[i] == 0)
        m.addConstr(in_degree[i] == 1)


# 执行模型
m.optimize()

#computeIIS()用于检查哪些约束是互相矛盾的,即去掉这些矛盾约束剩下的约束构成的问题是可行的
#m.computeIIS()
#m.write("model3.ilp")

#输出数据
for v in m.getVars():
    if v.x == 1:
        print('%s %g' % (v.varName, v.x))

print('Obj: %g' % m.objVal) # 查看单目标规划模型的目标函数值

3 输出结果

#========================print(data.head())========================
编号    X坐标(单位: m)     Y坐标(单位:m)   Z坐标(单位: m) 校正点类型
0   0      0.000000  50000.000000  5000.000000   A 点
1   1  33070.825831   2789.480761  5163.524680     0
2   2  54832.887019  49179.219108  1448.303791     1
3   3  77991.545911  63982.175286  5945.823038     0
4   4  16937.179535  84714.341903  5360.293002     0

#=======================print('data:\n',data)=======================
data:
         X坐标(单位: m)     Y坐标(单位:m)   Z坐标(单位: m) 校正点类型
0         0.000000  50000.000000  5000.000000   A 点
1     33070.825831   2789.480761  5163.524680     0
2     54832.887019  49179.219108  1448.303791     1
3     77991.545911  63982.175286  5945.823038     0
4     16937.179535  84714.341903  5360.293002     0
..             ...           ...          ...   ...
608   45789.201490  21191.207906   440.001872     1
609   94917.734859  82958.725321  6169.657707     0
610   14870.601682  95939.173362  8248.836185     0
611   93009.567446   4549.333260  7882.608297     1
612  100000.000000  59652.343380  5022.001164    B点

[613 rows x 4 columns]
----------------------------------------------------

#=======================print('\ndata的数据类型',type(data))=======================
data的数据类型 <class 'pandas.core.frame.DataFrame'>

#=======================#将dist中不同点,即不为0的权值以边的形式存储为Boundary列表后===========

初始权值数: 375156

删掉以AB为轴,半径10000外的点之后: 17292

校正条件筛选之后: 5097

删去距离大于 10 km 的同类顶点之间的有向边之后: 3737

删除方向与前进方向相反的有向边之后: 1809

#===========================将dist转换为tupledict类型、以及最大值max_dij的tupledict类型===================
<class 'dict'>
<class 'gurobipy.tupledict'>
max_dij= 139286.85817998415
<class 'dict'>
<class 'gurobipy.tupledict'>
#===========================模型求解结果===========================
Gurobi Optimizer version 9.0.3 build v9.0.3rc0 (win64)
Optimize a model with 5459 rows, 376995 columns and 13889 nonzeros
Model fingerprint: 0xf473aac1
Variable types: 1226 continuous, 375769 integer (375769 binary)
Coefficient statistics:
  Matrix range     [1e+00, 1e+04]
  Objective range  [7e+04, 1e+05]
  Bounds range     [1e+00, 1e+00]
  RHS range        [1e+00, 1e+04]
Presolve removed 3077 rows and 375344 columns
Presolve time: 0.28s
Presolved: 2382 rows, 1651 columns, 10685 nonzeros
Variable types: 121 continuous, 1530 integer (1530 binary)
Found heuristic solution: objective 2520005.5056

Root relaxation: objective 5.386343e+05, 195 iterations, 0.00 seconds

    Nodes    |    Current Node    |     Objective Bounds      |     Work
 Expl Unexpl |  Obj  Depth IntInf | Incumbent    BestBd   Gap | It/Node Time

     0     0 538634.323    0   14 2520005.51 538634.323  78.6%     -    0s
H    0     0                    1246717.7584 538634.323  56.8%     -    0s
H    0     0                    963471.90541 538634.323  44.1%     -    0s
H    0     0                    891937.45079 538634.323  39.6%     -    0s
H    0     0                    820316.25261 538944.757  34.3%     -    0s
     0     0 539292.457    0   16 820316.253 539292.457  34.3%     -    0s
     0     0 539344.334    0   18 820316.253 539344.334  34.3%     -    0s
H    0     0                    749581.67384 539344.334  28.0%     -    0s
H    0     0                    679412.93773 539344.334  20.6%     -    0s
     0     0 539776.400    0   14 679412.938 539776.400  20.6%     -    0s
H    0     0                    679367.18791 539776.400  20.5%     -    0s
     0     0 539809.111    0   14 679367.188 539809.111  20.5%     -    0s
     0     0 540309.127    0   16 679367.188 540309.127  20.5%     -    0s
     0     0 540361.414    0   20 679367.188 540361.414  20.5%     -    0s
     0     0 540768.325    0   14 679367.188 540768.325  20.4%     -    0s
     0     0 540974.035    0   14 679367.188 540974.035  20.4%     -    0s
     0     0 608968.758    0    8 679367.188 608968.758  10.4%     -    0s
     0     0 609166.422    0    8 679367.188 609166.422  10.3%     -    0s
     0     0 610045.789    0   14 679367.188 610045.789  10.2%     -    0s
     0     0 610045.789    0   10 679367.188 610045.789  10.2%     -    0s
     0     0 610045.789    0   17 679367.188 610045.789  10.2%     -    0s
     0     0 613720.884    0   20 679367.188 613720.884  9.66%     -    0s
     0     0 633510.455    0   18 679367.188 633510.455  6.75%     -    0s
     0     0 678556.660    0    6 679367.188 678556.660  0.12%     -    0s
*    0     0               0    679221.39430 679221.394  0.00%     -    0s

Explored 1 nodes (792 simplex iterations) in 0.69 seconds
Thread count was 12 (of 12 available processors)

Solution count 9: 679221 679367 679413 ... 2.52001e+06

Optimal solution found (tolerance 1.00e-04)
Best objective 6.792213943009e+05, best bound 6.792213943009e+05, gap 0.0000%

#===========================模型输出数据========================
x[0,503] 1
x[91,607] 1
x[250,340] 1
x[277,612] 1
x[294,91] 1
x[340,277] 1
x[503,294] 1
x[540,250] 1
x[607,540] 1
Obj: 679221

★佐佑思维公众号提供更多学习学术服务★ 二维码如下:

佐佑思维

如有问题欢迎公众号交流

参考:
1、参考链接1https://mp.weixin.qq.com/s/48AX6sEo5NXB1sWshDZp3Q
2、https://blog.csdn.net/qq_40678163/article/details/109407498

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值