节点操作接口
public interface Puzzle<P,M>{
//获取起始节点
P initialPosition();
//是否为目标节点
boolean isGoal(P position);
//合法的移动路径
Set<M> legalMoves(P position);
//移动之后的节点位置
P move(P position,M move)
}
节点
static class Node<P,M>{
//节点
final P pos;
//移动操作
final M move;
//前节点
fina Node<P,M> prev;
//获取从该节点到起始节点的移动操作列表
public List<M> asMoveList(){
List<M> solution = new LinkedList<M>();
for(Node<P,M> n=this;n.move != null;n=n.prev){
//因为移动是从起点开始,所以将移动操作放在列表的开头
solution.add(0,n.move);
}
return solution;
}
}
串行程序,深度优先
//该程序并不寻找最优,找到就停止
public class SequentialPuzzleSolver<P,M>{
private final Puzzle<P,M> puzzle;
//存放已经搜索过的节点
private final Set<P> seen = new HashSet<P>();
public List<M> solve(){
P pos = puzzle.initialPosition();
return search(new Node<P,M>(pos,null,null));
}
private List<M> search(Node<P,M> node){
if(!seen.contains(node.pos)){
seen.add(node.pos);
if(puzzle.isGoal(node.pos)) //目标节点
return node.asMoveList();
//遍历所有合法的路径
for(Move m:puzzle.legalMoves(node.pos)){
P pos = puzzle.move(node.pos,m);
Node<P,M> child = new Node<P,M>(pos,move,node);
List<M> result = search(child);
if(result != null)
return result;
}
}
//没找着
return null;
}
}
多线程广度搜索
//该广度搜索也是找到路径就返回,并不在意是否是最优节点。
//同时在找到后会关闭executor,需要将拒绝执行处理器设置为“抛弃已提交任务”
//在未找到解决方案时停止(将solution值设置为null)
public class ConcurrentPuzzleSolver<P,M>{
private final Puzzle<P,M> puzzle;
private final ExecutorService exec;
//为了线程安全,故将已经搜索过的路径放入concurrentMap,并采用putIfAbsent方法验证是否已搜索
private final ConcurrentMap<P,Boolean> seen;
//存放结果的闭锁对象
final ValueLatch<Node<P,M>> solution = new ValueLatch<Node<P,M>>();
//任务开启数
private final AtomicInteger taskCount = new AtomicInteger(0);
public List<M> solve() throws InterruptedException{
try{
P p = puzzle.initialPosition();
exec.execute(newTask(p,null,null) );
//getValue方法为阻塞实现,使用的是countDownLatch的await()方法
Node<P,M> solnNode = solution.getValue();
return (solunNode == null) ? null:solunNode.asMoveList();
}finally{
//将线程池guanbi
exec.shutdown();
}
}
protected Runnable newTask(P p,M m,Node<P,M> n){
return new SolverTask(p,m,n);
}
//搜索任务
class SolverTask extends Node<P,M> implements Runnable{
SolverTask(P p,M m,Node<P,M> n){
super(p,m,n);
//增加计数
taskCount.incrementAndGet();
}
public void run(){
try{
if(solution.isSet() || seen.putIfAbsent(pos,true) != null)
return;//已经找到或者已经搜索过
if(puzzle.isGoal(pos))
solution.setValue(this);//设置为已找到
else
for(M m:puzzle.legalMoves(pos))
exec.execute(newTask(puzzle.move(pos,m),m,this));
}finally{
//线程开启的任务数是0
if(taskCount.decrementAndGet() == 0)
solution.setValue(null);
}
}
}
//利用闭锁机制来检查程序是否找到结果,即阻塞的并携带结果的闭锁
public class ValueLatch<T>{
private synchronized T value = null;
//设置为1表示只要找到一个解答就停止
private final CountDownLatch done = new CountDownLatch(1);
public boolean isSet(){ //是否完成
return (done.getCount() == 0);
}
public synchronized void setValue(T newValue){
if(!isSet()){
value = newValue;
done.countDown(); //完成
}
}
public T getValues throws InterruptedException{
done.await(); //阻塞
synchronized(this){
return value;
}
}
}
}
该多线程版本可以添加等待时间限制,比如在getValue()中的await方法添加时间限制。
同时,该版本在关闭Executor时,要避免RejectedExecutionException,需要将拒绝执行处理器设置为“抛弃已提交的任务”