Operations Research (2): Optimization Algorithms课程作业代码及结果(调度|选址)

来源:Coursera课程Operations Research (2): Optimization Algorithms
Operations Research课程是National Taiwan University开设的运筹优化课程,共分成三个系列:Models and Applications;Optimization Algorithms和Theory,其中Part2主要介绍了一些求解算法,比如单纯形法,梯度下降,分支定界和启发式等,本文记录了该课程的结课小作业

1.调度问题

给定15个作业和加工时间,这些作业可以在3台机器上加工,作业之间存在冲突,即某两个作业不能在同一台机器上加工,要求给出作业加工的调度方案来最小化最大完工时间,使用Gurobi求解
在这里插入图片描述
首先建立数学模型
在这里插入图片描述
在这里插入图片描述

import gurobipy as gp
from gurobipy import GRB

jobs = [i for i in range(1, 16)]
machines = [i for i in range(1, 4)]

conflicts = {1:None, 2:(5,8), 
             3:None, 4:None, 
             5:(2,8), 6:(9,),
             7:(10,), 8:(2,5), 
             9:(6,), 10:(7,), 
             11:(15,), 12:None, 
             13:None, 14:None, 15:(11,)}

process = [7, 4, 6, 9, 12, 8, 10, 11, 8, 7, 6, 8, 15, 14, 3]

model = gp.Model()

# 决策变量
decision = model.addVars(jobs, machines, vtype=GRB.BINARY)
downtime = model.addVars(machines, vtype=GRB.CONTINUOUS)
makespan = model.addVar(vtype=GRB.CONTINUOUS)
#  目标函数
model.setObjective(makespan, GRB.MINIMIZE)
# 约束条件
model.addConstrs(makespan >= downtime[m] for m in machines)
model.addConstrs(downtime[m] == gp.quicksum(decision[j, m]*process[j-1] 
                 for j in jobs) for m in machines) 
model.addConstrs(gp.quicksum(decision[j, m] for m in machines) == 1
                 for j in jobs)
for j in jobs:
    if conflicts[j] is not None:
        for c in conflicts[j]:
            for m in machines:
                model.addConstr(decision[j, m] * decision[c, m] == 0) 

model.optimize()

for m in machines:
    print(downtime[m].x)
Optimal solution found (tolerance 1.00e-04)
Best objective 4.300000000000e+01, best bound 4.300000000000e+01, gap 0.0000%

最优目标值为43,与官方给定结果一致

2.选址问题

2.1 模型求解

有n个街区和m台救护车,已知街区之间的距离和人口,要求选定救护车的地址,使得最大加权消防时间最小,每个街区的加权消防时间=与所有救护车最近的距离✖️该街区的人口,比如只有一台救护车安排在街区1,那么对于街区2,加权消防时间=3✖️30=90,目标是最小化所有街区的加权消防时间最大值。
在这里插入图片描述
首先建立数学模型
在这里插入图片描述
在这里插入图片描述
基于上述模型使用python+Gurobi求解

import gurobipy as gp
from gurobipy import GRB
from itertools import combinations

districts = [i for i in range(1, 9)]
populations = [40, 30, 35, 20, 15, 50, 45, 60]
distances = [[0, 3, 4, 6, 8, 9, 8, 10],
             [3, 0, 5, 4, 8, 6, 12, 9],
             [4, 5, 0, 2, 2, 3, 5, 7],
             [6, 4, 2, 0, 3, 2, 5, 4],
             [8, 8, 2, 3, 0, 2, 2, 4],
             [9, 6, 3, 2, 2, 0, 3, 2],
             [8, 12, 5, 5, 2, 3, 0, 2],
             [10, 9, 7, 4, 4, 2, 2, 0]]

ambulance_nb = 3
# edges = [(i, j) for i, j in combinations(districts, 2)]
model = gp.Model()

# 决策变量
decision = model.addVars(districts, vtype=GRB.BINARY)
weight_district = model.addVars(districts, vtype=GRB.CONTINUOUS)
weight_max = model.addVar(vtype=GRB.CONTINUOUS)
close = model.addVars(districts, districts, vtype=GRB.BINARY)

# 目标函数
model.setObjective(weight_max, GRB.MINIMIZE)

# 约束
model.addConstrs(weight_max >= weight_district[i] for i in districts)
model.addConstr(gp.quicksum(decision[i] for i in districts) == ambulance_nb)

# 对街区i,街区集合districts里最近的那个一定有救护车(筛选边)
model.addConstrs(close[i, j] <= decision[j] for i in districts 
                for j in districts)
# 对街区i,街区集合districts里只有一个是最近的(筛选出唯一边)
model.addConstrs(gp.quicksum(close[i, j] for j in districts) == 1 for i in districts)

model.addConstrs(weight_district[i] >= close[i, j]*distances[i-1][j-1]*populations[i-1] 
                                                   for j in districts for i in districts)

model.optimize()
Optimal solution found (tolerance 1.00e-04)
Best objective 1.350000000000e+02, best bound 1.350000000000e+02, gap 0.0000%

在n=8,m=2时,最优目标值为135,与官方给定结果一致

2.2 启发式求解

对于同样问题,用启发式方法求解,启发式规则如下:首先选择第一辆救护车,假设选中街区1,获取一个最大加权消防时间,同样假设选中街区2在获取一个最大加权消防时间,在所有假设条件中,选择最大加权消防时间最小的假设,确定为第一辆救护车选址。在第一轮选择基础上,开启第二轮选择,以同样的方式决定。设置n=8,m=3,要求计算Gurobi求解和启发式求解两种方法的性能差距。
启发式代码如下:

populations = [40, 30, 35, 20, 15, 50, 45, 60]
distances = [[0, 3, 4, 6, 8, 9, 8, 10],
             [3, 0, 5, 4, 8, 6, 12, 9],
             [4, 5, 0, 2, 2, 3, 5, 7],
             [6, 4, 2, 0, 3, 2, 5, 4],
             [8, 8, 2, 3, 0, 2, 2, 4],
             [9, 6, 3, 2, 2, 0, 3, 2],
             [8, 12, 5, 5, 2, 3, 0, 2],
             [10, 9, 7, 4, 4, 2, 2, 0]]
target = 3
district = 8

# 记录
select = []
rest = [i for i in range(district)]
count = 0
final_max_weight = None

def get_mindist(start, select):
    res = 10000
    for end in select:
        res = min(res, distances[start][end] * populations[start])
    return res

# 循环
while count < target:
    flag = None 
    final_max_weight = None
    for d in rest:
        max_weight = 0 
        
        temp_select = select.copy()
        temp_rest = rest.copy()
        temp_select.append(d)
        temp_rest.remove(d)
        
        for a in temp_rest:
            min_dist = get_mindist(a, temp_select)
            if min_dist >= max_weight:
                max_weight = min_dist
        if final_max_weight == None or final_max_weight > max_weight:
            final_max_weight = max_weight
            flag = d
        
    select.append(flag)
    rest.remove(flag)
    count += 1
    print(select, rest, final_max_weight, count)
    print('*****')

print(final_max_weight)
[3] [0, 1, 2, 4, 5, 6, 7] 240 1
*****
[3, 0] [1, 2, 4, 5, 6, 7] 240 2
*****
[3, 0, 7] [1, 2, 4, 5, 6] 100 3
*****
100

在n=8,m=2时,最优目标值为100,与Gurobi结果一致

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值