optaplanner学习笔记(三)为规划问题建模

我们来学习一个如何建立一个业务模型,有哪些概念是需要我们掌握的,例如:ProblemFact、PlanningEntity、ProblemSolution,以及这些对象中的字段有什么作用。

3.1。这个类是问题事实ProblemFact还是计划实体PlanningEntity?

我们来看一下规划问题的数据集。,会认识到其中的对象,每个类别都可以归类为以下类别之一:

一个不相关的类:不被任何分数约束使用。从规划的角度来看,这些数据垃圾数据。

Problem Fact问题事实类:由分数约束使用,但在计划期间不会改变(只要问题保持不变)。例如:Bed, Room, Shift, Employee, Topic, Period, …​ 问题事实类的所有属性都是问题属性。

Problem Fact规划实体类:用于规划期间的分数约束和变化。例如:BedDesignation, ShiftAssignment, Exam, …​ 在规划期间发生变化的属性是Planning Variable规划变量。其他属性是问题属性。

问问自己:规划期间有哪些类发生变化? 哪个类有我想让求解器为我改变的变量?这个类是一个Planning Entity规划实体。大多数用例只有一个规划实体类。大多数用例的每个计划实体类也只有一个Planning Entity变量。

在实时规划中,即使问题本身发生变化,问题事实在规划过程中并没有真正改变,而是在规划之间发生变化(因为求解器暂时停止应用问题事实变化)。

在 OptaPlanner 中,所有Problem Fact问题事实和Planning Entity规划实体都是普通的 JavaBeans (POJO)。

3.2. 问题事实Problem Fact

一个Problem Fact问题事实类是任何带有 getter 方法的 JavaBean (POJO) ,在求解过程中都不会改变。例如在 n 个皇后中,列和行是问题事实:

public class Column {

    private int index;

    // ... getters
}
public class Row {

    private int index;

    // ... getters
}

问题事实当然可以引用其他问题事实:

public class Course {

    private String code;

    private Teacher teacher; // Other problem fact
    private int lectureSize;
    private int minWorkingDaySize;

    private List<Curriculum> curriculumList; // Other problem facts
    private int studentSize;

    // ... getters
}

问题事实类不需要任何 OptaPlanner 注解。

3.3. 规划实体Planning entity

3.3.1。规划实体注释
Planning entity规划实体是在求解过程中发生变化的 JavaBean (POJO),例如Queen更改为另一行。一个规划问题有多个规划实体,例如对于单个 n 个皇后问题,每个Queen都是一个规划实体。但通常只有一个规划实体类,例如Queen类。

规划实体类需要使用注解进行@PlanningEntity注解。

每个规划实体类都有一个或多个Planning Variable规划变量(可以是真实的或阴影的)。它还应该具有一个或多个定义属性。例如,在 n 皇后中,Queen由它定义,Column并且有一个计划变量Row。这意味着皇后的列在求解过程中永远不会改变,而它的行确实会改变。

@PlanningEntity
public class Queen {

    private Column column;

    // Planning variables: changes during planning, between score calculations.
    private Row row;

    // ... getters and setters
}

一个规划实体类可以有多个规划变量。例如课程时间表例子,一节课程,是有老师在某个时间段上在某个教室进行授课,所以Lesson它有两个规划变量,timeslot时段和room教室。

@PlanningEntity
public class Lesson {

    private Long id;

    private String subject;
    private String teacher;
    private String studentGroup;

    @PlanningVariable(valueRangeProviderRefs = "timeslotRange")
    private Timeslot timeslot;

    @PlanningVariable(valueRangeProviderRefs = "roomRange")
    private Room room;
    ....
}

求解器配置需要声明每个规划实体类:

<solver xmlns="https://www.optaplanner.org/xsd/solver" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="https://www.optaplanner.org/xsd/solver https://www.optaplanner.org/xsd/solver/solver.xsd">
  ...
  <entityClass>org.optaplanner.examples.nqueens.domain.Queen</entityClass>
  ...
</solver>

3.3.2. 规划实体难度
如果某些优化算法能够估计哪些规划实体更难规划,则它们的工作效率会更高。例如:在垃圾箱中,较大的物品更难装,在课程安排中,有更多学生的讲座更难安排,在 n 皇后中,中间的皇后更难放在板上。

不要尝试使用Comparator比较器来实现业务约束。它不会影响 score 函数:如果我们有无限的求解时间,返回的解决方案将是一样的。

为了达到某些实体在日程表中被安排得更早的目的,添加一个分数约束来改变分数函数,使其更倾向于这种解决方案。只有在可以使求解器更有效率的情况下,才考虑也增加规划实体的Comparator。

为了让启发式方法能够利用当前优化问题的特殊信息,需要@PlanningEntity注解设置 difficultyComparatorClass属性。

@PlanningEntity(difficultyComparatorClass = CloudProcessDifficultyComparator.class)
public class CloudProcess {
    // ...
}
public class CloudProcessDifficultyComparator implements Comparator<CloudProcess> {

    public int compare(CloudProcess a, CloudProcess b) {
        return new CompareToBuilder()
                .append(a.getRequiredMultiplicand(), b.getRequiredMultiplicand())
                .append(a.getId(), b.getId())
                .toComparison();
    }

}

或者,您也可以将 设置difficultyWeightFactoryClass为@PlanningEntity注释,以便您也可以从解决方案中访问其余的问题事实:

@PlanningEntity(difficultyWeightFactoryClass = QueenDifficultyWeightFactory.class)
public class Queen {
    // ...
}

难度应按升序执行:易实体较低,困难实体较高。例如,在 bin 包装中:小物品 < 中物品 < 大物品。

尽管大多数算法首先从更难的实体开始,但它们只是颠倒了顺序。

针对云平衡CloudBalance的例子,它的比较器就是将所需资源(cpuPower、memory、netWorkBandwidth)更多的进程优先排在前面,让求解器可以优先解决这些需求比较大的进程。
当前的规划变量都不应该被用来比较规划实体的难度。在构建启发式方法期间,这些变量无论如何都可能是空的。例如,不应该使用CloudProcess中的computer。

3.4. 规划变量
3.4.1。规划变量注释
Planning Variable规划变量是Planning Variable规划实体上的 JavaBean 属性(所以是一个是getter 和 setter)。它指向一个规划值,该值在规划期间会发生变化。例如,Queen的row是真正的规划变量。请注意,即使 Queen的row属性Row在计划期间更改为另一个,Row实例本身也不会更改。通常规划变量是真实的,但高级案例也可能有阴影。

真正的规划变量 getter 方法需要使用@PlanningVariable注解,并需要一个非空valueRangeProviderRefs属性。

@PlanningEntity
public class Queen {
    ...

    private Row row;

    @PlanningVariable(valueRangeProviderRefs = {"rowRange"})
    public Row getRow() {
        return row;
    }

    public void setRow(Row row) {
        this.row = row;
    }

}

该valueRangeProviderRefs属性定义了该规划变量的可能规划值。它引用一个或多个@ValueRangeProvider id’s。

@PlanningVariable 注释需要位于具有@PlanningEntity 注释的类中的属性上。塔在没有该注释的父类或子类将忽略它。

也可以将注解添加在字段上:

@PlanningEntity
public class Queen {
    ...

    @PlanningVariable(valueRangeProviderRefs = {"rowRange"})
    private Row row;

}

3.4.2. 可为空的规划变量
默认情况下,初始化的规划变量不能是null,所以一个初始化的解决方案永远不会对其任何规划变量使用空。在一个其它用例中,这可能会产生反作用。例如:在进行任务分配时,我们宁愿让低优先级的任务不被分配,而不是把它们分配给一个超负荷的工人。
如果要允许一个初始化的规划变量为空,请将nullable设置为true。

 @PlanningVariable(..., nullable = true)
    public Worker getWorker() {
        return worker;
    }

默认情况下,约束流过滤掉具有null计划变量的规划实体。使用forEachIncludingNullVars()来避免这种不需​​要的行为。

OptaPlanner会自动将数值null添加到数值范围中。无需在一个由ValueRangeProvider提供的List中添加null。

使用一个允许为null的规划变量意味着我们的分数计算要负责惩罚(甚至奖励)有null值的变量。

3.4.3. 什么时候考虑初始化规划变量?
如果计划变量的值不是null或变量是空的 ,则认为规划变量已初始化nullable。所以一个可为空的变量总是被认为是初始化的。

如果规划实体的所有规划变量都已初始化,则规划实体将被初始化。

如果解决方案的所有规划实体都已初始化,则解决方案将被初始化。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值