微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

限制平衡每日覆盖

如何解决限制平衡每日覆盖

这是我的问题表述的简化版本:

from ortools.sat.python import cp_model

def solve_shift_scheduling():
    num_employees = 6
    num_weeks = 4
    shifts = ['.','M']

    fixed_assignments = [
        (0,1,0),(0,2),5),(1,1),6),(2,(5,5)]
        
    requests = [
        (3,3,-2),(3,4,(4,2,-2)]

    weekly_cover_demands = [
        (4,),# Monday
        (4,# Tuesday
        (4,# Wednesday
        (4,# Thursday
        (4,# Friday
        (4,# Saturday
        (4,# Sunday
    ]
    
    employee_max_shifts = [
        (1,3),# employee 0
        (1,# employee 1
        (1,4),# employee 2
        (1,# employee 3
        (1,# employee 4
        (1,# employee 5
    ]

    num_days = num_weeks * 7
    num_shifts = len(shifts)

    model = cp_model.CpModel()

    work = {}
    for e in range(num_employees):
        for s in range(num_shifts):
            for d in range(num_days):
                work[e,s,d] = model.NewBoolVar('work%i_%i_%i' % (e,d))

    # Linear terms of the objective in a minimization context.
    obj_int_vars = []
    obj_int_coeffs = []
    obj_bool_vars = []
    obj_bool_coeffs = []

    # Exactly one shift per day.
    for e in range(num_employees):
        for d in range(num_days):
            model.Add(sum(work[e,d] for s in range(num_shifts)) == 1)

    # Fixed assignments.
    for e,d in fixed_assignments:
        model.Add(work[e,d] == 1)

    # Employee requests
    for e,d,w in requests:
        obj_bool_vars.append(work[e,d])
        obj_bool_coeffs.append(w)
        
    # Employee shifts constraint
    for e in range(num_employees):
         for w in range(num_weeks):
            s,max_shifts = employee_max_shifts[e]
            works = [work[e,d + w * 7] for d in range(7)]
            total_shifts = model.NewIntvar(0,max_shifts,'')
            model.Add(total_shifts==sum(works))
                
    # Cover constraints
    for s in range(1,num_shifts):
        for w in range(num_weeks):
            for d in range(7):
                works = [work[e,w * 7 + d] for e in range(num_employees)]
                demand = weekly_cover_demands[d][s - 1]
                worked = model.NewIntvar(0,demand,'')
                model.Add(worked == sum(works))
                name = 'under_demand: (shift={},week={},day={})'.format(s,w,d)
                under = model.NewIntvar(0,name)
                model.Add(under == demand - worked)
                obj_int_vars.append(under)
                obj_int_coeffs.append(20)
            
    # Objective
    model.Minimize(
        sum(obj_bool_vars[i] * obj_bool_coeffs[i]
            for i in range(len(obj_bool_vars))) +
        sum(obj_int_vars[i] * obj_int_coeffs[i]
            for i in range(len(obj_int_vars))))

    # Solve the model.
    solver = cp_model.cpsolver()
    solver.parameters.num_search_workers = 8
    solver.parameters.max_time_in_seconds = 600
    solution_printer = cp_model.ObjectiveSolutionPrinter()
    status = solver.solveWithSolutionCallback(model,solution_printer)
    
    # Print solution.
    if status == cp_model.OPTIMAL or status == cp_model.FEASIBLE:
        print()
        header = '          '
        for w in range(num_weeks):
            header += 'M T W T F S S '
        print(header)
        for e in range(num_employees):
            schedule = ''
            for d in range(num_days):
                for s in range(num_shifts):
                    if solver.BooleanValue(work[e,d]):
                        schedule += shifts[s] + ' '
            print('worker %i: %s' % (e,schedule))

此外,我还想添加一个尝试平衡每日覆盖范围的条件:

    # Balance Coverage 
    for s in range(1,num_shifts):
        for w in range(num_weeks):
            each_days_gap = []
            daily_demands = []
            for d in range(7):
                demand = weekly_cover_demands[d][s - 1]
                daily_demands.append(demand)
                works = [work[e,w*7+d] for e in range(num_employees)]
                demand_gap = model.NewIntvar(0,'')
                model.Add((demand - sum(works)) == demand_gap)
                each_days_gap.append(demand_gap)
            min_demand_gap = model.NewIntvar(0,max(daily_demands),'')
            max_demand_gap = model.NewIntvar(0,'')
            model.Addminequality(min_demand_gap,each_days_gap)
            model.AddMaxEquality(max_demand_gap,each_days_gap)
            name = 'target_gap: (shift={},week={})'.format(s,w)
            delta = model.NewIntvar(0,name)
            model.Add(delta == max_demand_gap-min_demand_gap)
            obj_int_vars.append(delta)
            obj_int_coeffs.append(10)

通过添加此规则,它会导致运行时间增加 20 倍,这意味着随着附加约束的添加,问题开始需要 10 多分钟才能解决。有没有更有效的方法来实现上述规则?

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。