Refactor in WriteAwareBruteSolver, add javadoc
Refactor WriteAwareBruteSolver constructor to guard against a situation in which the class could have an inconsistent internal state
This commit is contained in:
		@@ -7,21 +7,72 @@ import org.apache.commons.text.similarity.LevenshteinDistance;
 | 
				
			|||||||
/**
 | 
					/**
 | 
				
			||||||
 * This is a write-aware brute solver implementation that uses the levenshtein distance to sort passcode permutations
 | 
					 * This is a write-aware brute solver implementation that uses the levenshtein distance to sort passcode permutations
 | 
				
			||||||
 * into a tree. Walking the tree in-order (Likely a depth-first traversal internally) results in a naive nearest-neighbor
 | 
					 * into a tree. Walking the tree in-order (Likely a depth-first traversal internally) results in a naive nearest-neighbor
 | 
				
			||||||
 * optimization that generally performs well, but may perform poorly in some cases.
 | 
					 * optimization that generally performs well, but may perform poorly in some cases. Heuristic solutions exist that have
 | 
				
			||||||
 | 
					 * acceptable performance, and this is a more naive heuristic implementation.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This permutation optimization problem is NP-hard and a perfect brute-force solution has a worst-case running time that
 | 
					 * This permutation optimization problem is NP-hard and can be represented as the traveling salesperson problem.
 | 
				
			||||||
 * is super-polynomial. Heuristic solutions exist that have acceptable performance, and this is a more naive heuristic
 | 
					 * Representing passcode permutations as vertices and the levenshtein distance as edges in an undirected, weighted graph,
 | 
				
			||||||
 * implementation.
 | 
					 * an optimal solution is the shortest path satisfying a tour of the entire graph.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * A more advanced solution (based on Christofides, or another TSP heuristic) may result in more performance gains, but
 | 
				
			||||||
 | 
					 * the performance of this nearest-neighbor solution is adequate for the keypad sizes under test. A keypad size of 9
 | 
				
			||||||
 | 
					 * results in a graph of vertex-count 9!. It's unlikely performance gains from a more intelligent TSP solver would
 | 
				
			||||||
 | 
					 * offset the performance cost of building a graph and optimizing traversal.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
public class WriteAwareBruteSolver extends DumbBruteSolver {
 | 
					public class WriteAwareBruteSolver extends DumbBruteSolver {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private final TreeSet<IntegerLevenshtein> orderedTree;
 | 
					    private final TreeSet<IntegerLevenshtein> orderedTree;
 | 
				
			||||||
    private final Integer numpadSize;
 | 
					    private Integer numpadSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates an ordered tree on instantiation to be used as a cache for subsequent brute-force solves.
 | 
				
			||||||
 | 
					     * @param numpadSize The size of the padlock's numpad, used in generation of the internal ordered tree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
    public WriteAwareBruteSolver(int numpadSize) {
 | 
					    public WriteAwareBruteSolver(int numpadSize) {
 | 
				
			||||||
        orderedTree = new TreeSet<IntegerLevenshtein>();
 | 
					        orderedTree = new TreeSet<IntegerLevenshtein>();
 | 
				
			||||||
        this.numpadSize = numpadSize;
 | 
					        this.numpadSize = numpadSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.createOrderedTree(numpadSize);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Solves the padlock passed in to the method. The padlock's internal state should be correct after this method
 | 
				
			||||||
 | 
					     * runs.
 | 
				
			||||||
 | 
					     * @param padlockAdapter A padlock conforming to the PadlockAdapter contract
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    @Override
 | 
				
			||||||
 | 
					    public void solve(PadlockAdapter padlockAdapter) {
 | 
				
			||||||
 | 
					        int padlockNumpadSize = padlockAdapter.getNumpadSize();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.numpadSize != padlockNumpadSize) {
 | 
				
			||||||
 | 
					            this.createOrderedTree(padlockNumpadSize);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (IntegerLevenshtein integerLevenshtein : orderedTree) {
 | 
				
			||||||
 | 
					            if (this.checkPermutation(integerLevenshtein.getIntegerData(), padlockAdapter)) {
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Returns the size of the tree (the number of possible permutations)
 | 
				
			||||||
 | 
					     * @return the size of the ordered tree
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    public Integer getTreeSize() {
 | 
				
			||||||
 | 
					        return this.orderedTree.size();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Creates an ordered tree of possible passcode permutations
 | 
				
			||||||
 | 
					     * @param size The number of keys on the numpad
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    protected void createOrderedTree(int size) {
 | 
				
			||||||
 | 
					        if(this.numpadSize != size) {
 | 
				
			||||||
 | 
					            this.numpadSize = size;
 | 
				
			||||||
 | 
					            orderedTree.clear();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        Integer[] currentPermutation = new Integer[numpadSize];
 | 
					        Integer[] currentPermutation = new Integer[numpadSize];
 | 
				
			||||||
        for(int i = 0; i < numpadSize; i++) {
 | 
					        for(int i = 0; i < numpadSize; i++) {
 | 
				
			||||||
            currentPermutation[i] = i;
 | 
					            currentPermutation[i] = i;
 | 
				
			||||||
@@ -36,20 +87,4 @@ public class WriteAwareBruteSolver extends DumbBruteSolver {
 | 
				
			|||||||
            morePermutationsExist = this.calculateNextPermutation(currentPermutation, numpadSize);
 | 
					            morePermutationsExist = this.calculateNextPermutation(currentPermutation, numpadSize);
 | 
				
			||||||
        } while(morePermutationsExist);
 | 
					        } while(morePermutationsExist);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					 | 
				
			||||||
    public void solve(PadlockAdapter padlockAdapter) {
 | 
					 | 
				
			||||||
        int numpadSize = padlockAdapter.getNumpadSize();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Iterator<IntegerLevenshtein> iterator = orderedTree.iterator();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        while(iterator.hasNext()) {
 | 
					 | 
				
			||||||
            if(this.checkPermutation(iterator.next().getIntegerData(), padlockAdapter)) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    public Integer getTreeSize() {
 | 
					 | 
				
			||||||
        return this.orderedTree.size();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user