All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.k0zka.finder4j.backtrack.Backtrack Maven / Gradle / Ivy

There is a newer version: 0.7
Show newest version
package com.github.k0zka.finder4j.backtrack;

import java.util.Deque;
import java.util.Iterator;
import java.util.LinkedList;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Backtrack {

	static final class BackTrackState> {
		private final X state;

		private final Iterator steps;

		public BackTrackState(final X state, final Iterator steps) {
			super();
			this.state = state;
			this.steps = steps;
		}

		public X getState() {
			return state;
		}

		public Iterator getSteps() {
			return steps;
		}
	}

	private static final Logger logger = LoggerFactory
			.getLogger(Backtrack.class);

	/**
	 * Run a backtracking algorithm on a problem
	 * 
	 * @param state
	 * @param factory
	 * @param terminationStrategy
	 * @param listener
	 * @param step
	 */
	public static > void backtrack(
			final X state, final StepFactory factory,
			final TerminationStrategy terminationStrategy,
			final SolutionListener listener) {
		backtrack(state, factory, terminationStrategy, listener, null);
	}

	/**
	 * Run a backtracking algorithm on a problem
	 * 
	 * @param state
	 * @param factory
	 * @param terminationStrategy
	 * @param listener
	 * @param step
	 * @param parallelTrack
	 */
	public static > void backtrack(
			final X state, final StepFactory factory,
			final TerminationStrategy terminationStrategy,
			final SolutionListener listener,
			final ParallelTrack parallelTrack) {

		logger.debug("Starting backtrack");

		final Deque> stack = new LinkedList>();
		X head = state;
		// initial state
		BackTrackState btState = new BackTrackState(state, factory
				.produce(state).iterator());

		while (!terminationStrategy.stop(btState.getState())) {
			if (btState.getSteps().hasNext()) {
				// take next step
				final Step step = btState.getSteps().next();
				// just a short check if the other's have enough to do.
				// if taking multiple steps is enabled
				if (parallelTrack != null) {
					Iterator steps = btState.getSteps();
					fork(factory, terminationStrategy, listener, parallelTrack,
							head, steps);
				}

				logger.debug("Taking step {}", step);
				// save the state for possible return
				stack.push(btState);
				// create new state
				head = step.take(head);
				// move to new state
				btState = new BackTrackState(head, factory.produce(head)
						.iterator());
				logger.debug("Step ahead to {}");

				// check if new state is complete, notify if so
				if (head.isComplete()) {
					listener.onSolution(head);
				}
			} else {
				// no steps available, try to step back
				if (stack.isEmpty()) {
					// if stepping back is not possible, there is no solution
					logger.debug("no (more) solution possible, exiting");
					return;
				} else {
					// step back
					btState = stack.pop();
					head = btState.getState();
					logger.debug("Steping back to {}", head);
				}
			}
		}
		logger.debug("termination strategy decided to stop, exiting backtrack");
	}

	private static > void fork(
			final StepFactory factory,
			final TerminationStrategy terminationStrategy,
			final SolutionListener listener,
			final ParallelTrack parallelTrack, final X head,
			final Iterator steps) {
		// as long as
		// there are possible next steps
		// and computing resources available
		while (steps.hasNext() && parallelTrack.available()) {
			// get the next step
			final Step parallelStep = steps.next();
			// take a next step
			final X forkedStep = parallelStep.take(head);
			// leave the parallel process running
			parallelTrack.start(forkedStep, factory, terminationStrategy,
					listener);
		}
	}
}