From 26ff1404a1a1c89729dfc1d10983756f8878bd62 Mon Sep 17 00:00:00 2001 From: maddiebaka Date: Thu, 28 Mar 2024 19:13:31 -0400 Subject: [PATCH] Refactor in WriteAwareBruteSolver, add javadoc Refactor WriteAwareBruteSolver constructor to guard against a situation in which the class could have an inconsistent internal state --- .../interview/WriteAwareBruteSolver.java | 77 ++++++++++++++----- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/src/main/java/com/cleverthis/interview/WriteAwareBruteSolver.java b/src/main/java/com/cleverthis/interview/WriteAwareBruteSolver.java index eef0e53..41d99e6 100644 --- a/src/main/java/com/cleverthis/interview/WriteAwareBruteSolver.java +++ b/src/main/java/com/cleverthis/interview/WriteAwareBruteSolver.java @@ -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 * 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 - * is super-polynomial. Heuristic solutions exist that have acceptable performance, and this is a more naive heuristic - * implementation. + * This permutation optimization problem is NP-hard and can be represented as the traveling salesperson problem. + * Representing passcode permutations as vertices and the levenshtein distance as edges in an undirected, weighted graph, + * 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 { private final TreeSet 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) { orderedTree = new TreeSet(); 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]; for(int i = 0; i < numpadSize; i++) { currentPermutation[i] = i; @@ -36,20 +87,4 @@ public class WriteAwareBruteSolver extends DumbBruteSolver { morePermutationsExist = this.calculateNextPermutation(currentPermutation, numpadSize); } while(morePermutationsExist); } - - public void solve(PadlockAdapter padlockAdapter) { - int numpadSize = padlockAdapter.getNumpadSize(); - - Iterator iterator = orderedTree.iterator(); - - while(iterator.hasNext()) { - if(this.checkPermutation(iterator.next().getIntegerData(), padlockAdapter)) { - return; - } - } - } - - public Integer getTreeSize() { - return this.orderedTree.size(); - } }