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:
parent
152d9dc3ce
commit
26ff1404a1
@ -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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user