一、Optaplanner入门篇

本例并不适合零基础,对于optalanner不知道基本概念可以先去看看其他的文章。

推荐OptaPlanner学习记录1_optaplanner 排产-CSDN博客(我也是看完他的之后自己总结的)

问题示例:N皇后问题;在n×n的棋盘上,放置n个皇后,本例中n=8;并且皇后不能在同一行、列或者同一斜线;

①、对象建模:

        现在将x坐标与y坐标当做资源作为皇后的属性,皇后作为待分配坐标的类,还需要一个统一所有数据的规划类。

        1.设置id基类:

/**
 * @author Xiao Mi Feng
 * Created with IntelliJ IDEA
 * @date 2024-01-24 10:21
 */
public class AbstractPersistable implements Serializable {

    protected Integer id;

    protected AbstractPersistable() {
    }

    protected AbstractPersistable(int id) {
        this.id = id;
    }

    @PlanningId
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }


    @Override
    public String toString() {
        return getClass().getName().replaceAll(".*\\.", "") + "-" + id;
    }

}

        2.坐标X类(资源类):

/**
 * @author Xiao Mi Feng
 * Created with IntelliJ IDEA
 * @date 2024-01-24 10:21
 */
public class X {

    private Integer index;

    public X(Integer index) {
        this.index = index;
    }

    public X() {

    }

    public Integer getIndex() {
        return index;
    }

    public void setIndex(Integer index) {
        this.index = index;
    }


    @Override
    public String toString() {
        return "X{" +
                "index=" + index +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {return true;}
        if (o == null || getClass() != o.getClass()) {return false;}
        X x = (X) o;
        return Objects.equals(index, x.index);
    }

    @Override
    public int hashCode() {
        return Objects.hash(index);
    }
}

        3.坐标Y类(资源类):

/**
 * @author Xiao Mi Feng
 * Created with IntelliJ IDEA
 * @date 2024-01-24 10:21
 */
public class Y {
    private Integer index;

    public Y(Integer index) {
        this.index = index;
    }
    public Y(){

    }

    public Integer getIndex() {
        return index;
    }

    public void setIndex(Integer index) {
        this.index = index;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {return true;}
        if (o == null || getClass() != o.getClass()) {return false;}
        Y y = (Y) o;
        return Objects.equals(index, y.index);
    }

    @Override
    public int hashCode() {
        return Objects.hash(index);
    }

    @Override
    public String toString() {
        return "Y{" +
                "index=" + index +
                '}';
    }
}

        4.皇后类(分配后的实体类):

/**
 * @author Xiao Mi Feng
 * Created with IntelliJ IDEA
 * @date 2024-01-24 10:21
 */
@PlanningEntity
public class Nq extends AbstractPersistable {

    @PlanningVariable(valueRangeProviderRefs = {"xRange"})
    private X x;

    @PlanningVariable(valueRangeProviderRefs = {"yRange"})
    private Y y;


    public X getX() {
        return x;
    }

    public void setX(X x) {
        this.x = x;
    }

    public Y getY() {
        return y;
    }

    public void setY(Y y) {
        this.y = y;
    }

    public Nq(Integer id) {
        super(id);
    }

    public Nq() {

    }

    public Nq(X x, Y y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public String toString() {
        return "Nq{" +
                "x=" + x +
                ", y=" + y +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Nq nq = (Nq) o;
        return Objects.equals(x, nq.x) && Objects.equals(y, nq.y);
    }

    @Override
    public int hashCode() {
        return Objects.hash(x, y);
    }
}

        5.容器分配类(规划类):

/**
 * @author Xiao Mi Feng
 * Created with IntelliJ IDEA
 * @date 2024-01-24 10:21
 */
@PlanningSolution
public class NqAssignment extends AbstractPersistable {
    @PlanningScore
    public HardSoftScore score;

    @ProblemFactCollectionProperty
    @ValueRangeProvider(id = "xRange")
    public List<X> xList;
    @ProblemFactCollectionProperty
    @ValueRangeProvider(id = "yRange")
    public List<Y> yList;

    @PlanningEntityCollectionProperty
    public List<Nq> nqList;


    public HardSoftScore getScore() {
        return score;
    }

    public void setScore(HardSoftScore score) {
        this.score = score;
    }

    public NqAssignment() {
    }

    public NqAssignment(List<X> xList, List<Y> yList, List<Nq> nqList) {
        this.xList = xList;
        this.yList = yList;
        this.nqList = nqList;
    }


    public List<X> getxList() {
        return xList;
    }

    public void setxList(List<X> xList) {
        this.xList = xList;
    }


    public List<Y> getyList() {
        return yList;
    }

    public void setyList(List<Y> yList) {
        this.yList = yList;
    }


    public List<Nq> getNqList() {
        return nqList;
    }

    public void setNqList(List<Nq> nqList) {
        this.nqList = nqList;
    }

    @Override
    public String toString() {
        return "NqAssignment{" +
                "score=" + score +
                ", xList=" + xList +
                ", yList=" + yList +
                ", nqList=" + nqList +
                '}';
    }
}

规划类和分配后的实体类必须有ID;建模方式不止这一种,也有其他形式,主要看个人怎么定义规划类分配后的实体类资源类

  建模部分参考自:OptaPlanner学习记录2-CSDN博客

②、约束:(不存在软约束)    

结合上述条件,可得出下列约束:

1.横坐标不能相同;

2.纵坐标不能相同;

3.横纵坐标的差的绝对值不能相同;

4.横纵坐标的和不能相同;

上面约束全部为硬约束,硬约束不能被打破;软约束不应被打破。

③、注解解释

@PlanningId

维护id属性

@PlanningEntity

表示任务实体类,标注在类上表示最后问题的解会体现在该实体中

@PlanningVariable

标注在属性上,表示规划过程中,这个属性的值将被plan的,即通过调整这个属性来得到不同的方案

@PlanningSolution

标注在类上表示该类包括了所有解决方案,所以该类中的属性必须包含被分配的属性

@PlanningScore

标注在属性上表示分值

@ProblemFactCollectionProperty

标注来属性上,表示所有的规划情况

@ValueRangeProvider

id与@PlanningVariable注解中的valueRangeProviderRefs对应,得到的解决方案会分配在被标有@PlanningVariable的属性里

@PlanningEntityCollectionProperty

需要被规划的任务:在本例中表示皇后,因为皇后需要被分配X与Y坐标

④、求解器写法:

        1.ConstraintProvider写法:增量评分,ConstraintStream编码方式

/**
 * @author Xiao Mi Feng
 * Created with IntelliJ IDEA
 * @date 2024-01-24 11:19
 */
    public class NqAssignmentConstraintProvider implements ConstraintProvider {
    @Override
    public Constraint[] defineConstraints(ConstraintFactory constraintFactory) {
        return new Constraint[]{
            //1.横坐标不能相同
                goConflict(constraintFactory),
            //2.纵坐标不能相同
                arrangeConflict(constraintFactory),
            //3.横纵坐标的差的绝对值不能相同
                biasConflict(constraintFactory),
            //4.横纵坐标的和不能相同
                biasTwoConflict(constraintFactory)
        };
    }

    Constraint goConflict(ConstraintFactory constraintFactory) {

        return constraintFactory.fromUniquePair(Nq.class,
                        //不能在同一行
                        Joiners.equal(Nq::getX)
                )
                .penalize("Go conflict", HardSoftScore.ONE_HARD);

    }

    Constraint biasConflict(ConstraintFactory constraintFactory) {

        return constraintFactory.fromUniquePair(Nq.class,
                        //不能在同一斜线
                        Joiners.equal((t) -> Math.abs(t.getX().getIndex() - t.getY().getIndex()))
                )
                .penalize("Bias conflict", HardSoftScore.ONE_HARD);

    }

    Constraint arrangeConflict(ConstraintFactory constraintFactory) {
        return constraintFactory.fromUniquePair(Nq.class,
                        //不能在同一列
                        Joiners.equal(Nq::getY)
                )
                .penalize("Arrange conflict", HardSoftScore.ONE_HARD);
    }

    Constraint biasTwoConflict(ConstraintFactory constraintFactory) {

        return constraintFactory.fromUniquePair(Nq.class,
                        //不能在同一斜线
                        Joiners.equal((t) -> t.getX().getIndex() + t.getY().getIndex())
                )
                .penalize("BiasTwo conflict", HardSoftScore.ONE_HARD);

    }
}

        2.EasyScoreCalculator写法:简易评分,简单易懂

/**
 * @author Xiao Mi Feng
 * Created with IntelliJ IDEA
 * @date 2024-01-24 12:58
 */
public class NqAssignmentEasyScoreCalculator implements EasyScoreCalculator<NqAssignment,
        HardSoftScore> {
    @Override
    public HardSoftScore calculateScore(NqAssignment nqAssignment) {
        List<Nq> nqList = nqAssignment.nqList;
        //硬约束分值
        int hardScore = 0;
        for (Nq a : nqList) {
            for (Nq b : nqList) {
                if (a.getId().compareTo(b.getId()) != 0 && a.getX() != null && a.getY() != null && b.getX() != null && b.getY() != null) {
                    if (a.getX().equals(b.getX())) {
                        //如果x坐标相同就减分
                        hardScore--;
                    }
                    if (a.getY().equals(b.getY())) {
                        //如果y坐标相同就减分
                        hardScore--;
                    }
                    if (Math.abs(a.getX().getIndex() - a.getY().getIndex()) == Math.abs(b.getX().getIndex() - b.getY().getIndex())) {
                        //如果同一斜线就减分
                        hardScore--;
                    }
                    if (a.getX().getIndex()+a.getY().getIndex() == b.getX().getIndex()+b.getY().getIndex()){
                        //如果同一斜线就减分
                        hardScore--;
                    }
                }
            }
        }
        //System.out.println("hardScore = " + hardScore);
        //软约束分值
        int softScore = 0;
        return HardSoftScore.of(hardScore, softScore);
    }
}

二者并无本质上的区别,都是通过计算分值来筛选出得分最高的,只是写法上有些不同。

ConstraintProvider写法:stream流编码方式。

EasyScoreCalculator写法:简单写法,逻辑一目了然。

还有一种写法:通过Drools规则引擎来描述约束并进行评分。比较古老(现在已经可以完全脱离Drools规则引擎),不推荐使用,想要了解可以查阅资料。

⑤、main方法执行

/**
 * @author Xiao Mi Feng
 * Created with IntelliJ IDEA
 * @date 2024-01-24 14:26
 */
public class NqAssignmentApp {
    public static void main(String[] args) {
        SolverFactory<NqAssignment> objectSolverFactory = SolverFactory.create(new SolverConfig()
                .withSolutionClass(NqAssignment.class)//待规划的容器
                .withEntityClasses(Nq.class)//规划后的容器
                .withConstraintProviderClass(
                        NqAssignmentConstraintProvider.class)//与EasyScoreCalculator求解器取其一即可
                .withTerminationSpentLimit(Duration.ofSeconds(10))//指定运算时间(s),不指定则默认一直计算
        );


        NqAssignment checkerboard = generateDemoData();

        Solver<NqAssignment> solver = objectSolverFactory.buildSolver();
        NqAssignment solve = solver.solve(checkerboard);

        System.out.println("solve = " + solve);

    }

    //设置八个皇后和64个坐标
    static NqAssignment generateDemoData() {
        int queue = 8;
        List<X> xList = new ArrayList<>();
        List<Y> yList = new ArrayList<>();
        List<Nq> nqList = new ArrayList<>();
        for (int i = 1; i <= queue; i++) {
            xList.add(new X(i));
            yList.add(new Y(i));
            nqList.add(new Nq(i));
        }
        return new NqAssignment(xList, yList, nqList);
    }
}

⑥、运行结果

{
    "nqList":[
        {
            "id":1,
            "x":{
                "index":8
            },
            "y":{
                "index":7
            }
        },
        {
            "id":2,
            "x":{
                "index":7
            },
            "y":{
                "index":2
            }
        },
        {
            "id":3,
            "x":{
                "index":3
            },
            "y":{
                "index":5
            }
        },
        {
            "id":4,
            "x":{
                "index":4
            },
            "y":{
                "index":3
            }
        },
        {
            "id":5,
            "x":{
                "index":6
            },
            "y":{
                "index":6
            }
        },
        {
            "id":6,
            "x":{
                "index":1
            },
            "y":{
                "index":4
            }
        },
        {
            "id":7,
            "x":{
                "index":5
            },
            "y":{
                "index":1
            }
        },
        {
            "id":8,
            "x":{
                "index":2
            },
            "y":{
                "index":8
            }
        }
    ]
}

结合上面结果可以得出下表:所有皇后不在同一行,列和同一斜线

1

2

3

4

5

6

7

8

🤡

🤡

🤡

🤡

🤡

🤡

🤡

🤡

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
OptaPlanner是一个开源的约束优化引擎,可以在求解约束优化问题时提供支持。它使用Java编写,并且具有强大的能力来解决各种问题,如旅行商问题、调度问题、排产问题等。OptaPlanner的目标是通过确定可能的解决方案,找到最佳解决方案。 OptaPlanner采用了基于元启发式算法的解决方法,如模拟退火、遗传算法、局部搜索等。它还提供了丰富的插件和扩展点,使开发人员能够自定义算法和评分规则,并将其集成到自己的应用程序中。 OptaPlanner使用约束规划模型来描述问题,并通过改善解决方案来满足约束条件。它的求解过程可以分为初始化、迭代和终止三个阶段。在初始化阶段,OptaPlanner生成一组初始解决方案,通常使用随机算法。在迭代阶段,它不断优化当前解决方案,直到达到最优解或超过预定的迭代次数。在终止阶段,OptaPlanner返回最佳解决方案。 OptaPlanner可以应用于各种领域,如物流、交通、资源调度等。它具有很好的扩展性和灵活性,可以根据具体问题的特点进行定制和调整。它还提供了丰富的文档和示例程序,使开发人员能够快速上手并解决实际问题。 总之,OptaPlanner是一个强大的约束优化引擎,可以帮助解决各种约束优化问题。它的功能丰富而灵活,易于使用和定制。无论是学术研究还是实际应用,OptaPlanner都是一个值得考虑的优秀选择。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值