first concurrent solution
This commit is contained in:
parent
502458d373
commit
7c146ce8ad
|
@ -26,10 +26,10 @@ final public class Solver extends JPanel {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
// The default size of the labyrinth (i.e. unless program is invoked with size arguments):
|
// The default size of the labyrinth (i.e. unless program is invoked with size arguments):
|
||||||
private static final int DEFAULT_WIDTH_IN_CELLS = 100;
|
private static final int DEFAULT_WIDTH_IN_CELLS = 1000;
|
||||||
private static final int DEFAULT_HEIGHT_IN_CELLS = 100;
|
private static final int DEFAULT_HEIGHT_IN_CELLS = 1000;
|
||||||
|
|
||||||
private static final int N_RUNS_HALF = 5; // #runs will be 2*N_RUNS_HALF + 1
|
private static final int N_RUNS_HALF = 10; // #runs will be 2*N_RUNS_HALF + 1
|
||||||
|
|
||||||
// The grid defining the structure of the labyrinth
|
// The grid defining the structure of the labyrinth
|
||||||
private final Labyrinth labyrinth;
|
private final Labyrinth labyrinth;
|
||||||
|
@ -38,7 +38,8 @@ final public class Solver extends JPanel {
|
||||||
private boolean[][] visited; // initialized in solve()
|
private boolean[][] visited; // initialized in solve()
|
||||||
private AtomicBoolean[][] visitedAtomic;
|
private AtomicBoolean[][] visitedAtomic;
|
||||||
|
|
||||||
private static final int DISTANCE_PER_TASK = 20;
|
// determined by trial and error
|
||||||
|
private static final int DISTANCE_PER_TASK = DEFAULT_WIDTH_IN_CELLS * DEFAULT_HEIGHT_IN_CELLS / 128;
|
||||||
|
|
||||||
private Point[] solution = null; // set to solution path once that has been computed
|
private Point[] solution = null; // set to solution path once that has been computed
|
||||||
|
|
||||||
|
@ -124,16 +125,19 @@ final public class Solver extends JPanel {
|
||||||
|
|
||||||
public Point[] solveConcurrently() {
|
public Point[] solveConcurrently() {
|
||||||
// dummy origin direction for start
|
// dummy origin direction for start
|
||||||
PointAndDirection start = new PointAndDirection(labyrinth.getStart(), Direction.N);
|
PointAndDirection start = new PointAndDirection(labyrinth.getStart(), null);
|
||||||
|
|
||||||
visitedAtomic = new AtomicBoolean[labyrinth.getWidth()][labyrinth.getHeight()];
|
visitedAtomic = new AtomicBoolean[labyrinth.getWidth()][labyrinth.getHeight()];
|
||||||
|
for (int i = 0; i < visitedAtomic.length; i++) {
|
||||||
ArrayDeque<PointAndDirection> backtrackStack = new ArrayDeque<PointAndDirection>();
|
for (int j = 0; j < visitedAtomic[i].length; j++) {
|
||||||
|
visitedAtomic[i][j] = new AtomicBoolean(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ForkJoinPool pool = ForkJoinPool.commonPool();
|
ForkJoinPool pool = ForkJoinPool.commonPool();
|
||||||
|
|
||||||
|
ArrayDeque<Point> pathSoFar = new ArrayDeque<>();
|
||||||
ConcurrentSolverTask t = new ConcurrentSolverTask(backtrackStack, start);
|
ConcurrentSolverTask t = new ConcurrentSolverTask(start, pathSoFar, pool);
|
||||||
Point[] result = pool.invoke(t);
|
Point[] result = pool.invoke(t);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -142,55 +146,136 @@ final public class Solver extends JPanel {
|
||||||
private class ConcurrentSolverTask extends RecursiveTask<Point[]> {
|
private class ConcurrentSolverTask extends RecursiveTask<Point[]> {
|
||||||
private ArrayDeque<PointAndDirection> backtrackStack;
|
private ArrayDeque<PointAndDirection> backtrackStack;
|
||||||
private PointAndDirection start;
|
private PointAndDirection start;
|
||||||
private int initialStackSize;
|
|
||||||
private HashSet<Point> visitedThisTask;
|
private HashSet<Point> visitedThisTask;
|
||||||
public ConcurrentSolverTask(ArrayDeque<PointAndDirection> backtrackStack, PointAndDirection start) {
|
private ArrayDeque<Point> pathSoFar;
|
||||||
this.backtrackStack = backtrackStack;
|
private ForkJoinPool pool;
|
||||||
|
private ArrayList<ConcurrentSolverTask> subtasks;
|
||||||
|
public ConcurrentSolverTask(PointAndDirection start, ArrayDeque<Point> pathSoFar, ForkJoinPool pool) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.initialStackSize = backtrackStack.size();
|
this.pathSoFar = pathSoFar;
|
||||||
this.visitedThisTask = new HashSet<>();
|
this.pool = pool;
|
||||||
|
|
||||||
|
this.backtrackStack = new ArrayDeque<>();
|
||||||
|
this.subtasks = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Point[] compute() {
|
public Point[] compute() {
|
||||||
PointAndDirection current = this.start;
|
int currentLength = 0;
|
||||||
|
Point current = this.start.getPoint();
|
||||||
|
|
||||||
if (labyrinth.isDestination(current.getPoint())) {
|
Point next;
|
||||||
return backtrackStackToPath(backtrackStack);
|
Direction[] dirs = Direction.values();
|
||||||
|
|
||||||
|
while (!labyrinth.isDestination(current) && !Thread.interrupted()) {
|
||||||
|
// System.out.println("Visiting " + current);
|
||||||
|
|
||||||
|
next = null;
|
||||||
|
|
||||||
|
for (Direction dir : dirs) {
|
||||||
|
|
||||||
|
// don't check direction of origin
|
||||||
|
if (
|
||||||
|
current.equals(this.start.getPoint())
|
||||||
|
&& dir.equals(start.getDirectionToBranchingPoint())
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!current.getDirectionToBranchingPoint().equals(Direction.N)) {
|
// check if labyrinth has passage in this direction
|
||||||
if () {
|
if (!labyrinth.hasPassage(current, dir)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Point neighbour = current.getNeighbor(dir);
|
||||||
|
|
||||||
|
// avoid blind alleys
|
||||||
|
if (
|
||||||
|
labyrinth.isBlindAlley(neighbour, dir.opposite)
|
||||||
|
&& !labyrinth.isDestination(neighbour)
|
||||||
|
) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if that cell has been visited
|
||||||
|
// set visitedAtomic to true if it has not been visited
|
||||||
|
if (!visitedAtomic[neighbour.x][neighbour.y].compareAndSet(false, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// System.out.println("Found unvisited neighbour: " + neighbour);
|
||||||
|
if (currentLength >= DISTANCE_PER_TASK) {
|
||||||
|
// if we have reached the distance limit, create a subtask
|
||||||
|
ArrayDeque<Point> subPath = this.pathSoFar.clone();
|
||||||
|
subPath.addLast(current);
|
||||||
|
ConcurrentSolverTask subtask = new ConcurrentSolverTask(
|
||||||
|
new PointAndDirection(neighbour, dir.opposite),
|
||||||
|
subPath,
|
||||||
|
this.pool
|
||||||
|
);
|
||||||
|
subtask.fork();
|
||||||
|
subtasks.add(subtask);
|
||||||
|
} else {
|
||||||
|
if (next == null) {
|
||||||
|
// visit first unvisited neighbour next
|
||||||
|
next = neighbour;
|
||||||
|
// System.out.println("Visiting next.");
|
||||||
|
} else {
|
||||||
|
// push all other unvisited neighbours on backtrack stack to be checked later
|
||||||
|
this.backtrackStack.push(new PointAndDirection(neighbour, dir.opposite));
|
||||||
|
// System.out.println("Pushing to stack.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// at least one unvisited neighbour found
|
||||||
|
if (next != null) {
|
||||||
|
pathSoFar.addLast(current);
|
||||||
|
current = next;
|
||||||
|
currentLength++;
|
||||||
|
}
|
||||||
|
if (next == null) {
|
||||||
|
// no unvisited neighbour found
|
||||||
|
// backtrack if possible
|
||||||
|
if (this.backtrackStack.isEmpty()) {
|
||||||
|
// no solution found from this point
|
||||||
|
|
||||||
|
// check for solutions in subtasks
|
||||||
|
// System.out.println("End of task reached - waiting for " + subtasks.size() + " subtasks");
|
||||||
|
for (ConcurrentSolverTask subtask : subtasks) {
|
||||||
|
Point[] subtaskResult = subtask.join();
|
||||||
|
if (subtaskResult != null) {
|
||||||
|
// found a solution in a subtask
|
||||||
|
return subtaskResult;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no solution found
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean visit(PointAndDirection p) {
|
PointAndDirection pd = this.backtrackStack.pop();
|
||||||
if (visitedThisTask.contains(p.getPoint())) {
|
current = pd.getPoint();
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Point lastPoint = backtrackStack.getLast().getPoint();
|
|
||||||
if (Math.abs(lastPoint.y - p.getPoint().y) + Math.abs(lastPoint.x - p.getPoint().x) > 1) {
|
|
||||||
System.err.println("Cannot visit point " + p.getPoint() + " because it is not adjacened to " + lastPoint);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!visitedAtomic[p.getPoint().x][p.getPoint().y].compareAndSet(false, true)) return false;
|
|
||||||
|
|
||||||
backtrackStack.add(p);
|
// Remove the dead end from the top of pathSoFar, i.e. all cells after branchingPoint:
|
||||||
visitedThisTask.add(p.getPoint());
|
Point branchingPoint = current.getNeighbor(pd.getDirectionToBranchingPoint());
|
||||||
|
while (!pathSoFar.isEmpty() && !pathSoFar.peekLast().equals(branchingPoint)) {
|
||||||
return true;
|
pathSoFar.removeLast();
|
||||||
}
|
currentLength--;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Point[] backtrackStackToPath(ArrayDeque<PointAndDirection> backtrackStack) {
|
|
||||||
Point[] result = new Point[backtrackStack.size()];
|
|
||||||
for (int i = 0; i < result.length; i++) {
|
|
||||||
result[backtrackStack.size() - 1] = backtrackStack.remove().getPoint();
|
|
||||||
}
|
}
|
||||||
return result;
|
}
|
||||||
|
// parent thread has found solution or other interrupt
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
pathSoFar.addLast(current);
|
||||||
|
|
||||||
|
// solution found, interrupt subtasks
|
||||||
|
for (ConcurrentSolverTask subtask : subtasks) {
|
||||||
|
subtask.cancel(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return pathSoFar.toArray(new Point[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -321,7 +406,7 @@ private static void displayLabyrinth(Solver solver) {
|
||||||
}
|
}
|
||||||
|
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
solver.solution = solver.solve();
|
solver.solution = solver.solveConcurrently();
|
||||||
long endTime = System.currentTimeMillis();
|
long endTime = System.currentTimeMillis();
|
||||||
|
|
||||||
if (solver.solution == null)
|
if (solver.solution == null)
|
||||||
|
|
Loading…
Reference in New Issue