【教程18】optaplanner重复规划

OptaPlanner的重复规划允许在问题事实变化时进行动态规划,包括备份规划、超约束规划、连续规划和实时规划。超约束规划通过预留空间处理无法完全分配的情况,而连续规划用于处理未来不确定性的规划。实时规划则处理问题事实的实时变化,通过ProblemChange接口进行解决方案的更新。多阶段规划将复杂问题分解为多个阶段,每个阶段有自己的求解器配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

optaplanner重复规划

1. 重复规划简介

在执行解决方案之前或期间,用于创建解决方案的问题事实可能会发生变化。为了降低问题事实变化的风险而延迟规划并不理想,因为不完整的计划比没有计划更可取。

以下示例演示了需要根据不可预测的变化修改规划解决方案的情况:

  • 未预料到的事实变化
    • 被安排到班次的员工请病假。
    • 预定起飞的飞机遇到技术故障延误。
    • 机器或车辆之一损坏。

无法立即分配所有实体

  • 留下一些未分配,例如:
    • 同一时间有10个班次需要分配,但只有九个员工可以处理班次。

对于这种类型的规划,使用超约束规划。

未知的长期未来事实

  • 例如:
    • 下两周的医院入院人数可靠,但第三周和第四周不太可靠,第五周及以后还不值得进行规划。

这个问题可以通过连续规划来解决。

问题事实不断变化

  • 使用实时规划。

更多的CPU时间会得到更好的规划解决方案。

OptaPlanner允许您在遇到不可预见变化时尽早开始规划,因为优化算法支持部分规划的解决方案。这就是所谓的重复规划。

2. 备份规划

备份规划通过添加额外的得分约束来为出现问题时提供规划空间。这样,在计划内创建备份计划。

备份规划的示例如下:

  • 创建额外的得分约束。例如:
    • 将员工指定为备用员工(每个同时有10个班次)。
    • 在每个部门保留一张病床。

当发生不可预见事件时,更改规划问题。例如,如果一个员工请病假:

  • 删除病假员工并将其班次保持未分配状态。
  • 重新启动规划,从该解决方案开始,此时解决方案的得分已经发生了变化。
  • 构造启发式方法填充新创建的间隙(可能使用备用员工),元启发式方法进一步优化。

3. 超约束规划

当无法分配所有规划实体时,最好尽可能多地分配实体而不违反硬约束。这称为超约束规划。

默认情况下,OptaPlanner会分配所有规划实体,超载规划值,因此会违反硬约束。有两种方法可以避免这种情况:

  • 使用可空的规划变量,使一些实体未分配。
  • 添加虚拟值以捕获未分配的实体。

3.1. 使用可空变量的超约束规划

如果我们使用可空变量处理超约束规划,则超载的实体将保持未分配状态。

overconstrainedPlanning

为了实现这一点:

  • 切换得分类型,添加一个得分级别(通常介于硬和软级别之间)。
  • 将规划变量设置为可空。
  • 在新级别上添加得分约束(通常是中等约束),以惩罚未分配实体的数量(或其加权和)。

3.2. 使用虚拟值的超约束规划

在超约束规划中,了解缺少哪些资源通常是很有用的。在具有虚拟值的超约束规划中,解决方案指示需要购买哪些资源。

为了实现这一点:

  • 通过切换得分类型添加额外的得分级别(通常是硬级别和软级别之间的中等级别)。
  • 添加一定数量的虚拟值。确定好计算该数量的公式可能比较困难:
    • 不要添加太多,否则会降低求解器的效率。
    • 重要的是不要添加太少,否则会导致无法实现的解决方案。
  • 在新级别上添加得分约束(通常是中等约束),以惩罚虚拟分配实体的数量(或其加权和)。
  • 可选地,将所有软约束更改为忽略虚拟分配实体。

4. 连续规划(窗口规划)

连续规划是一种技术,可以同时规划一个或多个即将到来的规划周期,并每月、每周、每天、每小时甚至更频繁地重复该过程。然而,由于时间是无限的,规划所有未来时间段是不可能的。

continuousPlanningEmployeeRostering

在上面的员工排班示例中,我们每四天重新规划一次。每次,我们实际上规划了12天的窗口,但只发布了前四天的计划,这足够稳定,可以与员工共享,以便他们可以相应地安排社交生活。

continuousPlanningPatientAdmissionSchedule

在上面的医院床位规划示例中,请注意11月1日的原始规划和11月5日的新规划之间的差异:其中一些问题事实(F、H、I、J、K)在此期间发生了变化,从而导致不相关的规划实体(G)也发生了变化。

规划窗口可以分为几个阶段:

  • 历史

    • 不可变的过去时间段。它只包含固定的实体。
    • 最近的历史实体也可能影响适用于可移动实体的得分约束。例如,在护士排班中,连续工作了三个历史周末的护士不应该再连续被安排到三个周末,因为她每月需要一个休息周末。
    • 不要将所有历史实体加载到内存中:尽管固定实体不会影响求解性能,但当数据增长到数年时,它们可能会导致内存问题。只加载那些可能仍然影响当前约束的实体,确保有足够的安全边界。
  • 已发布

    • 已发布的即将到来的时间段。它们只包含固定和/或半可移动的规划实体。
    • 已发布计划已与业务共享。例如,在护士排班中,护士将使用此计划来安排个人生活,因此他们需要提前大约3周的发布通知。普通规划不会更改计划的这部分。
    • 更改此计划后会带来不便,但是由于异常情况,我们仍然需要做出这些更改(例如有人请病假),在最小化不可中断的重新规划的同时更改计划。
  • 草稿

    • 已发布时间段之后的即将到来的时间段,可以自由更改。它们包含可移动的规划实体,除非有其他原因(例如用户指定)而被固定。
    • 草稿的第一部分称为最终草稿,并将其发布,因此这些规划实体可以最后一次更改。发布频率(例如每周一次)确定了从草稿到已发布的时间段数量。
    • 草稿的后期时间段可能会在以后的规划中再次发生变化,特别是如果其中一些问题事实在那时发生了变化(例如护士Ann不想在其中某天工作)。
    • 尽管这些后期规划实体可能会再次发生很大变化,但我们不能将它们留到以后,因为那样我们可能会陷入困境。例如,在员工排班中,我们可能会让所有罕见的技能员工在最后5天工作,这不会降低那一周的得分,但会使我们无法提供下周的可行计划。因此,草稿长度需要比首先发布的部分更长。
    • 通常情况下,不会与业务共享草稿,因为它太不稳定,并且只会产生虚假的期望。然而,它存储在数据库中,并用作下一个求解器的起点。
  • 未规划(超出范围)

    • 不在当前规划窗口中的规划实体。
    • 如果规划窗口太小以规划所有实体,则需要处理超约束规划。
    • 如果时间是一个规划变量,则规划窗口的大小是动态确定的,此时未规划阶段不适用。

continuousPublishingWithRotation

4.1. 固定规划实体

固定规划实体在求解过程中不会更改。用户常常使用固定规划实体将一个或多个特定分配固定下来,并强制OptaPlanner围绕这些固定分配进行调度。

4.1.1. 使用@PlanningPin固定规划实体

要固定一些规划实体,请在规划实体类的布尔型getter或字段上添加@PlanningPin注释。如果实体被固定到其当前规划值,则该布尔值为true,否则为false。

在布尔型上添加@PlanningPin注释:

@PlanningEntity
public class Lecture {
   
   

    private boolean pinned;
    ...

    @PlanningPin
    public boolean isPinned() {
   
   
        return pinned;
    }

    ...
}

上面的示例中,如果pinned为true,则不会将讲座分配给其他时间段或教室(即使当前的period和room字段为null)。

4.1.2. 配置PinningFilter

要固定一些规划实体,请添加一个PinningFilter,如果实体被固定,则返回true;如果可移动,则返回false。这比@PlanningPin方法更灵活、更冗长。

例如,在护士排班示例中:

添加PinningFilter:

public class ShiftAssignmentPinningFilter implements PinningFilter<NurseRoster, ShiftAssignment> {
   
   

    @Override
    public boolean accept(NurseRoster nurseRoster, ShiftAssignment shiftAssignment)
### OptaPlanner在车辆路径规划和订单排单方面的教程与示例 OptaPlanner(现已更名为Timefold[^1])是一个强大的开源优化框架,广泛应用于解决复杂的调度和规划问题。以下是关于OptaPlanner在车辆路径规划(VRP, Vehicle Routing Problem)以及订单排单方面的教程和使用指南的详细说明。 #### 1. 车辆路径规划(VRP)教程 车辆路径规划OptaPlanner的一个经典应用场景,旨在优化多个车辆配送货物的路径,以减少总行驶距离或时间。以下是一些关键点: - **快速启动示例**:OptaPlanner提供了多个内置示例,其中包括车辆路径规划问题。用户可以通过下载OptaPlanner的源代码并运行`vrp`模块来了解其基本实现。 - **核心概念**: - `@PlanningEntity`:用于定义需要优化的实体,例如送货地点(Customer)。 - `@PlanningVariable`:表示可以调整的变量,例如每个送货地点分配给哪辆车。 - `Score Calculation`:通过定义硬约束(如每辆车的最大容量)和软约束(如总行驶距离最小化),计算解决方案的质量。 ```java @PlanningEntity public class Customer { private Location location; private int demand; // 需求量 private Vehicle vehicle; // 分配的车辆 @PlanningVariable(valueRangeProviderRefs = "vehicleRange") public Vehicle getVehicle() { return vehicle; } public void setVehicle(Vehicle vehicle) { this.vehicle = vehicle; } } ``` - **搜索策略**:OptaPlanner支持多种搜索策略,包括模拟退火、禁忌搜索和遗传算法等。选择合适的策略对于复杂问题尤为重要[^3]。 #### 2. 订单排单示例 订单排单涉及将多个订单合理分配给不同的资源(如员工或机器),以满足特定的时间窗口和优先级要求。以下是如何使用OptaPlanner实现这一目标的示例: - **重复规划**:如果订单动态变化,可以使用`@PlanningPin`注释固定某些订单的位置,避免不必要的重新计算[^2]。 ```java @PlanningEntity public class Order { private boolean pinned; private Resource resource; // 分配的资源 @PlanningPin public boolean isPinned() { return pinned; } @PlanningVariable(valueRangeProviderRefs = "resourceRange") public Resource getResource() { return resource; } public void setResource(Resource resource) { this.resource = resource; } } ``` - **约束定义**:通过Drools规则文件定义硬约束和软约束,确保解决方案既可行又高效。 ```java rule "MaxOrdersPerResource" when accumulate(Order(resource == $r && !isPinned(), $c: 1); sum($c) > maxOrdersAllowed) then scoreHolder.addHardConstraintMatch(kcontext, -1); end ``` #### 3. 实际应用中的最佳实践 为了提高OptaPlanner在车辆路径规划和订单排单中的性能,建议遵循以下最佳实践: - **模型简化**:尽量减少不必要的约束,专注于关键业务逻辑[^3]。 - **调试与优化**:利用OptaPlanner的日志功能监控求解过程,并根据统计信息调整参数。 - **扩展性**:设计可扩展的模型,以便在未来添加更多约束或变量时能够轻松适应。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值