Optaplanner 至少一个发生约束

问题描述 投票:0回答:1

我正在尝试实施约束,以便如果具有特定名称的元素至少有一次未分配,则解决方案将受到惩罚。

我尝试使用

GroupBy()
但它不起作用:

Constraint atLeastOne(ConstraintFactory cf) {
        return cf
                .forEach(Element.class)
                .groupBy(Element::getName, ConstraintCollectors.countDistinct())
                .filter((elementName, count) -> elementName == "foo" && count < 1)
                .penalize(HardSoftScore.ONE_HARD)
                .asConstraint("At least one occurency");
    }

我的想法是,如果不存在名称为“foo”的元素,则过滤器不会返回任何内容,因此不会强制执行约束。

我也尝试过使用

ifNotExists()

Constraint atLeastOne(ConstraintFactory cf) {
        return cf
                .forEach(Element.class)
                .ifNotExists(Element.class, Joiners.filtering((a, b) -> a.getName() == "foo" && b.getName() == "foo"))
                .penalize(HardSoftScore.ONE_HARD)
                .asConstraint("At least one occurency");
    }

问题是使用

ifNotExists()
也会惩罚所有带有
a.getName() != "foo"

的元素

有什么想法让它发挥作用吗?

optaplanner
1个回答
0
投票

在约束流中,

groupby()
只有在至少有一个输入元组时才会产生结果。这意味着“至少有 X”约束必须作为两个单独的约束来实现:

  • 一个约束来惩罚没有元素满足约束的情况
  • 一个约束来惩罚某些(但不够)元素满足约束的情况(这不是必需的,因为最小值为 1,但如果最小值为 2 或更多则需要)

由于您使用的是分组键,因此您需要一种方法来访问“没有元素满足约束”情况下的所有可能键。创建一个包含所有可能名称的

ElementName
集合,并将其作为
@ProblemFactCollectionProperty
放在您的
@PlanningSolution
上。然后约束看起来像这样:

Constraint atLeastOne(ConstraintFactory cf) {
    return cf
            .forEach(ElementName.class)
            .filter(elementName -> elementName.getName().equals("foo"))
            .ifNotExists(Element.class, Joiners.equal(ElementName::getName, Element::getName))
            .penalize(HardSoftScore.ONE_HARD)
            .asConstraint("At least one occurency");
}
© www.soinside.com 2019 - 2024. All rights reserved.