经过第一天的学习,已经有一定基础,接下来会研究一些官方给出来的例子。下边会使用源码项目中的例子。
N queens
n 个皇后放置在一个 n×n 的棋盘上,使皇后彼此之间不相互攻击,已知在同一行、同一列或者同一条斜线上,皇后之间都会相互攻击。一般n求的是8。
请注意,大多数n皇后难题有多个正确的解决方案。我们将专注于找到给定n的唯一正确解,而不是找到给定n的可能正确解的数量。
这里先不关心官方例子是怎么写的,我们先看自己能否根据快速入门来解决问题。分析问题,我们需要先对问题进行建模,下边是我自己画的模型分析。计划实体会因为问题事实的变化而变化,而问题事实我是可以提前确认的(n=8的时候,x和y就是1-8).
下边我们将自己来解决这个N皇后问题。
POM
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner</artifactId>
<version>7.8.0.Final</version>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.optaplanner</groupId>
<artifactId>optaplanner-core</artifactId>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<image>
<builder>paketobuildpacks/builder-jammy-base:latest</builder>
</image>
</configuration>
</plugin>
</plugins>
</build>
</project>
由于计划事实需要用id区分,创建AbstractPersistable
public class AbstractPersistable implements Serializable, Comparable<AbstractPersistable> {
protected Long id;
protected AbstractPersistable() {
}
protected AbstractPersistable(long id) {
this.id = id;
}
@PlanningId
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public int compareTo(AbstractPersistable other) {
return new CompareToBuilder().append(getClass().getName(), other.getClass().getName()).append(id, other.id)
.toComparison();
}
@Override
public String toString() {
return getClass().getName().replaceAll(".*\\.", "") + "-" + id;
}
}
创建计划实体:nq类 他代表一个皇后的坐标
@PlanningEntity
public class nq extends AbstractPersistable {
@PlanningVariable(valueRangeProviderRefs={"xRange"})
public x x;
@PlanningVariable(valueRangeProviderRefs={"yRange"})
public y y;
public nq() {
}
public nq(Long id) {
super(id);
}
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;
}
}
x和y类分别代表x轴和y轴
public class x extends AbstractPersistable {
//n横坐标
public Integer index;
public x() {
}
public x(Long id,Integer index) {
super(id);
this.index = index;
}
public x(Integer index) {
this.index = index;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
}
public class y extends AbstractPersistable {
//n横坐标
public Integer index;
public y() {
}
public y(Long id,Integer index) {
super(id);
this.index = index;
}
public y(Integer index) {
this.index = index;
}
public Integer getIndex() {
return index;
}
public void setIndex(Integer index) {
this.index = index;
}
}
以上模型就建立好了,现在还需要创建一个类来承接问题所有的实体nqAssignment
@PlanningSolution
public class nqAssignment extends AbstractPersistable {
public HardSoftScore score;
public List<x> xList;
public List<y> yList;
public List<nq> nqList;
@PlanningScore
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;
}
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "xRange")
public List<x> getxList() {
return xList;
}
public void setxList(List<x> xList) {
this.xList = xList;
}
@ProblemFactCollectionProperty
@ValueRangeProvider(id = "yRange")
public List<y> getyList() {
return yList;
}
public void setyList(List<y> yList) {
this.yList = yList;
}
@PlanningEntityCollectionProperty
@ValueRangeProvider(id = "nqList")
public List<nq> getNqList() {
return nqList;
}
public void setNqList(List<nq> nqList) {
this.nqList = nqList;
}
}
接下来在resources创建drl文件和配置文件(这里不懂不要紧,上边的模型才是重点)
taskassignmentConfiguration2.xml
<?xml version="1.0" encoding="UTF-8"?>
<solver>
<!-- Domain model configuration -->
<solutionClass>com.example.demo.optaPlanner.nqSolver.nqAssignment</solutionClass>
<entityClass>com.example.demo.optaPlanner.nqSolver.nq</entityClass>
<!-- Score configuration -->
<scoreDirectorFactory>
<scoreDrl>rules3.drl</scoreDrl>
</scoreDirectorFactory>
<!-- Optimization algorithms configuration -->
<termination>
<secondsSpentLimit>10</secondsSpentLimit>
</termination>
</solver>
rules3.drl
package com.example.demo.solver;
import org.optaplanner.core.api.score.buildin.hardsoft.HardSoftScoreHolder;
import com.example.demo.optaPlanner.nqSolver.nq;
import com.example.demo.optaPlanner.nqSolver.nqAssignment;
import com.example.demo.optaPlanner.nqSolver.x;
import com.example.demo.optaPlanner.nqSolver.y;
global HardSoftScoreHolder scoreHolder;
//不能是同一行
rule "xline"
when
nq( $id:getId(), getX() != null, $index:getX().getIndex())
nq( getId() != $id ,getX() != null, getX().getIndex() == $index )
then
scoreHolder.addHardConstraintMatch(kcontext, -10000);
end
//不能是同一列
rule "yline"
when
nq( $id:getId(), getY() != null, $index:getY().getIndex())
nq( getId() != $id ,getY() != null, getY().getIndex() == $index )
then
scoreHolder.addHardConstraintMatch(kcontext, -10000);
end
//不是同一个斜线
rule "xyline"
when
nq( $id:getId(), getY() != null,getX() != null, $xindex1:getX().getIndex(), $yindex1:getY().getIndex())
nq( $id != getId(), getY() != null,getX() != null, $xindex2:getX().getIndex(), $yindex2:getY().getIndex() )
eval(Math.abs($xindex2 - $xindex1) == Math.abs($yindex2 - $yindex1))
then
scoreHolder.addHardConstraintMatch(kcontext, -10000);
end
到此所有的准备工作已经完结,下边创建启动类
AppNq
package com.example.demo.optaPlanner;
import com.example.demo.optaPlanner.domain.Order;
import com.example.demo.optaPlanner.domain.OrderAssignment;
import com.example.demo.optaPlanner.domain.PlanLine;
import com.example.demo.optaPlanner.nqSolver.nq;
import com.example.demo.optaPlanner.nqSolver.nqAssignment;
import com.example.demo.optaPlanner.nqSolver.x;
import com.example.demo.optaPlanner.nqSolver.y;
import org.optaplanner.core.api.solver.Solver;
import org.optaplanner.core.api.solver.SolverFactory;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author wcr
* @create 2023-11-16 17:48
*/
public class AppNq {
public static void main(String[] args) {
startPlan();
}
private static void startPlan(){
InputStream ins = AppNq.class.getResourceAsStream("/taskassignmentConfiguration2.xml");
SolverFactory<nqAssignment> solverFactory = SolverFactory.createFromXmlInputStream(ins);
Solver<nqAssignment> solver = solverFactory.buildSolver();
List<x> xList = new ArrayList<>();
xList.add(new x(1L,1));
xList.add(new x(2L,2));
xList.add(new x(3L,3));
xList.add(new x(4L,4));
xList.add(new x(5L,5));
xList.add(new x(6L,6));
xList.add(new x(7L,7));
xList.add(new x(8L,8));
List<y > yList= new ArrayList<>();
yList.add(new y(1L,1));
yList.add(new y(2L,2));
yList.add(new y(3L,3));
yList.add(new y(4L,4));
yList.add(new y(5L,5));
yList.add(new y(6L,6));
yList.add(new y(7L,7));
yList.add(new y(8L,8));
List<nq> nqList = new ArrayList<>();
nqList.add(new nq(1L));
nqList.add(new nq(2L));
nqList.add(new nq(3L));
nqList.add(new nq(4L));
nqList.add(new nq(5L));
nqList.add(new nq(6L));
nqList.add(new nq(7L));
nqList.add(new nq(8L));
nqAssignment unassignment = new nqAssignment(xList,yList,nqList);
nqAssignment assigned = solver.solve(unassignment);//启动引擎
System.out.println("exit!");
}
}
运行程序,就会得到答案啦,记住,答案并不唯一,我们这里求到解就算成功,这里就靠我们自己的力量解决了一个问题,回头打开官方例子的解决办法,大差不差,官方例子中多了许多对于算法的优化。后续学习会慢慢解析。