optaplanner学习笔记(四)Planning Value

Planning Value规划值

Planning Value规划值是真正规划变量的可能值。通常,规划值是一个问题事实ProblemFact,但它也可以是任何对象,例如 一个 double。它甚至可以是另一个规划实体,甚至可以是一个由规划实体和问题事实都实现的接口。规划值范围是一个规划变量的可能规划值的集合。该集合可以是固定的(例如 1、2、3或4行)或随机的(例如0.0 和 1.0之间的任何双数)。

PlanningValue的范围

规划变量的value范围由@ValueRangeProvider注释来定义的。一个@ValueRangeProvider注释总是有一个属性id,它被@PlanningVariable’s 的属性valueRangeProviderRefs所引用。

@PlanningEntity
public class Lesson {

    private Long id;
    ......
    @PlanningVariable(valueRangeProviderRefs = "timeslotRange")
    private Timeslot timeslot;
    ......
}

@ValueRangeProvider此注解可以位于两种类型的方法上:

1、在注解@PlanningSolution的类上:所有规划实体共享相同的数据集合。
2、在PlanningEntity规划实体上:每个规划实体的值范围不同,这种情况不太常见。

@ValueRangeProvider 注释需要位于具有@PlanningSolution 或@PlanningEntity 注释的类中的一个方法或者属性上。没有这些注释的父类或子类将忽略它。

该方法的返回类型可以是三种类型:

1、Collection:值范围由其可能值的集合(通常是一个List)来定义 。

2、Array:值范围由其可能值的数组定义。

3、ValueRange:值范围由其边界定义。这种情况不太常见。

ValueRangeProvider在Solution类

同一计划实体类的所有实例共享该计划变量的同一组可能的计划值。这是配置值范围的最常用方法。

该@PlanningSolution的实现方法返回一个集合Collection(或 一个 ValueRange)。该集合中的任何值Collection都是该规划变量的可能规划值。

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

    @ValueRangeProvider(id = "rowRange")
    public List<Row> getRowList() {
        return rowList;
    }

}

注意:该集合Collection(或ValueRange)不能包含值null,甚至对于可为空的计划变量也不行。

ValueRangeProvider关于规划实体

每个PlanningEntity规划实体都有自己的规划变量的值范围(一组可能的规划值)。例如,如果一个老师永远不能在不属于他部门的房间里教书,那么该老师的讲座可以将他们的房间价值范围限制在他的部门的房间内。

@PlanningVariable(valueRangeProviderRefs = {"departmentRoomRange"})
public Room getRoom() {
    return room;
}

@ValueRangeProvider(id = "departmentRoomRange")
public List<Room> getPossibleRoomList() {
    return getCourse().getTeacher().getDepartment().getRoomList();
}

永远不要用它来强制执行软约束(或者当问题可能没有可行的解决方案时,也不要用硬约束)。例如:除非没有其他办法,否则老师不能在不属于他部门的房间里教学。在这种情况下,老师不应该被限制在他的房间范围内(因为有时没有其他办法就只剩其它部门的教室,他必须要给学生上课)。

通过专门限制一个规划实体的数值范围,实际上是在创建一个内在的硬约束。这样做的好处是可以大大减少可能的解决方案的数量;但是,它也可以剥夺优化算法的自由,使其暂时打破该约束,以摆脱局部最优的情况。

计划实体不应使用其他计划实体来确定其价值范围。那只会试图让规划实体自己解决规划问题并干扰优化算法。

每个实体都有自己的List实例,除非多个实体具有相同的值范围。例如,如果教师 A 和 B 属于同一部门,则他们使用相同的List实例。此外,每个都List包含同一组计划值实例的子集。例如,如果部门 A 和 B 都可以使用房间 X,那么它们的List实例包含相同的Room实例。

计划实体上的AValueRangeProvider消耗的内存比ValueRangeProvider解决方案上的多,并禁用某些自动性能优化。

计划实体上的AValueRangeProvider当前与链式变量不兼容。

ValueRangeFactory工程类

支持返回一个由ValueRangeFactory构建的ValueRange或CountableValueRange,而不是一个集合。

@ValueRangeProvider(id = "delayRange")
public CountableValueRange<Integer> getDelayRange() {
    return ValueRangeFactory.createIntValueRange(0, 5000);
}

ValueRange使用的内存要少得多,因为它只保存边界。在上面的示例中, 一个集合需要保存所有5000个整数,而不仅仅是两个边界。

此外,还可以指定一个增量单位(incrementUnit),例如,如果您必须以 200 块为单位购买股票:

    boolean: 一个布尔范围。
    
    int: 一个 32 位整数范围。
    
    long: 一个 64 位整数范围。
    
    double: 一个 64 位浮点范围,只支持随机选择(因为它没有实现CountableValueRange)。
    
    BigInteger: 任意精度的整数范围。
    
    BigDecimal: 小数点范围。默认情况下,增量单位是边界范围内的最低非零值。
    
    Temporal(例如LocalDate, LocalDateTime, ...​):时间范围。

结合ValueRangeProviders

可以组合值范围提供程序,例如:

 @PlanningVariable(valueRangeProviderRefs = {"companyCarRange", "personalCarRange"})
    public Car getCar() {
        return car;
    }
 @ValueRangeProvider(id = "companyCarRange")
    public List<CompanyCar> getCompanyCarList() {
        return companyCarList;
    }

    @ValueRangeProvider(id = "personalCarRange")
    public List<PersonalCar> getPersonalCarList() {
        return personalCarList;
    }

3.5.3. PlanningValue权重

如果某些优化算法能够估计哪些规划值更强,可以优先提供,也就是更有可能满足一个规划实体,那么工作起来就会性能会提高。
例如:在垃圾箱包装中,更大的容器更有可能容纳物品,而在课程安排中,更大的房间不太可能打破学生容量限制。通常,规划价值强度的效率增益远小于规划实体难度的效率增益。

不要尝试使用计划价值强度来实施业务约束。它不会影响 score 函数:如果我们有无限的求解时间,返回的解将是相同的。

要影响得分函数,请添加得分约束。只有在可以使求解器提高性能的情况下,才考虑增加规划值强度。

为了让启发式方法利用该对象的特定信息,需要@PlanningVariable注解设置一个强度比较器类。

@PlanningVariable(..., strengthComparatorClass = CloudComputerStrengthComparator.class)
public CloudComputer getComputer() {
    return computer;
}
public class CloudComputerStrengthComparator implements Comparator<CloudComputer> {

    public int compare(CloudComputer a, CloudComputer b) {
        return new CompareToBuilder()
                .append(a.getMultiplicand(), b.getMultiplicand())
                .append(b.getCost(), a.getCost()) // Descending (but this is debatable)
                .append(a.getId(), b.getId())
                .toComparison();
    }

}

如上例子,在提供Computer值时,优先提供资源比较大的Computer。

如果您在同一值范围内有多个规划值类,则strengthComparatorClass需要实现Comparator一个共同的父类(例如Comparator)并能够处理这些不同类的比较实例。

或者,您也可以将 strengthWeightFactoryClass为@PlanningVariable注释,因此您也可以从解决方案中访问其余的问题事实:

@PlanningVariable(…, strengthWeightFactoryClass = RowStrengthWeightFactory.class)
public Row getRow() {
return row;
}

有关详细信息,请参阅排序选择。

强度应按升序执行:较弱的值较低,较强的值较高。例如在箱子包装中:小容器 < 中容器 < 大容器。

任何计划实体中的当前计划变量状态都不应该用于比较计划值。在构建启发式期间,这些变量可能是null. 例如,任何row变量Queen都不能用于确定 a 的强度Row。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值