92、船员调度

文章介绍了CrewScheduling问题的数据集,涉及10个测试问题,目标是确定在不同任务条件下最少需要的船员数,同时考虑鲁棒性和最短移动时间。作者提供了一个Python实现,用于切分任务和生成可能的组合进行精确解的集合划分。
摘要由CSDN通过智能技术生成

数据集

Crew scheduling

第一目标船员最少,即极限情况下至少需要多少船员才能保证稳定运行,延展可以考虑鲁棒性的问题和最短移动时间

pro = """
CrewScheduling
There are currently 10 data files.

These data files are the ten test problems from J.E.Beasley
and B.Cao "A tree search algorithm for the crew scheduling 
problem" European Journal of Operational Research 94 (1996) 
pp517-526.

The test problems solved in that paper are available in
the files csp50, csp100, csp150, csp200, csp250, csp300, 
csp350, csp400, csp450 and csp500 (where the number for
each csp file is the number of tasks).

The format of these data files is:
number of tasks (N), time limit
for each task i (i=1,...,N): start time, finish time
for each transition arc between two tasks (i and j):
i, j, cost of transition from i to j

The value of the optimal solution for each of these data files
for a varying number of crews is given in the above paper.

The largest file is csp500 of size 250Kb (approximately).
The entire set of files is of size 900Kb (approximately).
"""

import pyscipopt
import re
import time
import math

#记录每个函数执行的时间
loop = 1
def timeLog(f):
    def wrapper(*args, **kw):
        global loop
        now = time.perf_counter()
        res = f(*args, **kw)
        print("%s-%s:"%(loop, f.__name__), time.perf_counter()-now)
        loop += 1
        return res
    return wrapper

class Task:
    def __init__(self, id, timeStart, timeEnd):
        self.id = id
        self.timeStart = timeStart
        self.timeEnd = timeEnd

    def __repr__(self):
        return str([self.id, self.timeStart, self.timeEnd])

class Case:

    def __init__(self, name, n, tMax, tasks, T):
        self.name = name
        self.n = n
        self.tMax = tMax
        self.tasks = tasks
        self.T = T

    def __repr__(self):
        return str([self.name, self.n, self.tMax, self.tasks])

class SubTask:
    def __init__(self, id, taskId, timeStart, timeEnd):
        self.id = id
        self.taskId = taskId
        self.timeStart = timeStart
        self.timeEnd = timeEnd

    def __repr__(self):
        return str([self.id, self.taskId, self.timeStart, self.timeEnd])

def dataRead():
    data_paths = [r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp50.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp100.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp150.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp200.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp250.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp300.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp350.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp400.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp450.txt",
                   r"/Users/zhangchaoyu/PycharmProjects/pythonProject/OR_Library/CrewScheduling/data/csp500.txt"]

    cases = []
    for path in data_paths:
        f = open(path)
        row = f.readline().replace('\n', '').strip()
        row = [int(w) for w in re.split(" ", row)]
        n, tMax = row[0], row[1]
        tasks = {}
        for i in range(n):
            row = f.readline().replace('\n', '').strip()
            row = [int(w) for w in re.split(" ", row)]
            timeStart, timeEnd = row[0], row[1]
            tasks[i+1] = Task(i+1, timeStart, timeEnd)
        T = {}
        row = f.readline().replace('\n', '').strip()
        while row != "":
            row = [int(w) for w in re.split(" ", row)]
            T[row[0], row[1]] = row[2]
            row = f.readline().replace('\n', '').strip()
        cases.append(Case(re.split("/", path)[-1], n, tMax, tasks, T))
    return cases

#将任务按照时间限制打散
def subTaskGenerate(case, gap=60):
    subTasks = []
    loop = 1
    for task in case.tasks.values():
        for t in range(task.timeStart, task.timeEnd, gap):
            subTasks.append(SubTask(loop, task.id, t, min(t+gap, task.timeEnd)))
            loop += 1
    return subTasks

#将时间线能够衔接上的组合在一起
def subTaskCombined(case, subTasks):
    T = case.T
    lines = []
    for i in range(len(subTasks)):
        si = subTasks[i]
        #单个任务
        lines.append([si])
        for j in range(i+1, len(subTasks)):
            sj = subTasks[j]
            #如果两个任务之间可以走过去,走过去的时间够,且时间约束也满足
            if ((si.taskId, sj.taskId) in T and si.timeEnd + T[si.taskId, sj.taskId] <= sj.timeStart or si.taskId == sj.taskId) \
                    and si.timeStart + case.tMax >= sj.timeEnd:
                lines.append([si, sj])
                for k in range(j+1, len(subTasks)):
                    sk = subTasks[k]
                    if ((sj.taskId, sk.taskId) in T and sj.timeEnd + T[sj.taskId, sk.taskId] <= sk.timeStart or sj.taskId == sk.taskId) \
                            and si.timeStart + case.tMax >= sk.timeEnd:
                        lines.append([si, sj, sk])
                    for l in range(k + 1, len(subTasks)):
                        sl = subTasks[l]
                        if ((sk.taskId, sl.taskId) in T and sk.timeEnd + T[sk.taskId, sl.taskId] <= sl.timeStart or sk.taskId == sl.taskId) \
                                and si.timeStart + case.tMax >= sl.timeEnd:
                            lines.append([si, sj, sk, sl])
    return lines

##保证每个subtask都被分配了
#假设有m个任务组,n个任务,aij表示第i个任务组里是否含有第j个任务,那么即min sum(xi) s.t. sum(aij*xi) = 1
def setPartition(lines, subTasks, time_limit, printLog=False):
    S = {s.id for s in subTasks}
    A = {i: {s.id for s in lines[i]} for i in range(len(lines))}
    model = pyscipopt.Model("setPartition")
    X = {i:model.addVar(vtype="B", name="X[%s]" % i) for i in range(len(lines))}
    model.setObjective(pyscipopt.quicksum(X[i] for i in range(len(lines))), "minimize")

    #每个任务都被分配
    for id in S:
        model.addCons(pyscipopt.quicksum(X[i] for i in range(len(lines)) if id in A[i]) == 1)

    # 设置求解时间
    model.setRealParam("limits/time", time_limit)
    if not printLog:
        model.hideOutput()
    model.optimize()
    print("\ngap:", model.getGap())

    # 拿结果
    X1 = {i: round(model.getVal(X[i])) for i in range(len(lines))}

    return X1

#先将可能的组合列出来,然后作集合划分,尽量保证集合划分是精确解,后面想办法提升前面列举的全面性(由于组合太多应该不可能列举完)
def f(case):
    gap = 120
    #将任务切成小任务
    subTasks = subTaskGenerate(case, gap)
    #将能衔接上的小任务组合起来
    lines = subTaskCombined(case, subTasks)
    #保证每个subtask都被分配了
    linesRes = setPartition(lines, subTasks, time_limit=100, printLog=True)
    linesRes = [lines[k] for k,v in linesRes.items() if v == 1]
    return len(linesRes)

if __name__ == '__main__':
    cases = dataRead()
    # 第一目标是最少船员,即极限情况下至少需要多少个船员才能保证事情的稳定运行,延展的化可以考虑鲁棒性的问题和最短移动时间
    case = cases[3]
    times = [task.timeEnd-task.timeStart for task in case.tasks.values()]
    times.sort()
    # 先将可能的组合列出来,然后作集合划分,尽量保证集合划分是精确解,后面想办法提升前面列举的全面性(由于组合太多应该不可能列举完)
    n = f(case)

第一目标是最少船员,即极限情况下至少需要多少个船员才能保证事情的稳定运行,延展的化可以考虑鲁棒性的问题和最短移动时间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值