import java.util.HashMap; import java.util.LinkedList; import java.util.StringTokenizer; /** * Breadth First Search Implementation for checking block crossing * distances of permutations * @author Martin Fink */ public class BlockCrossings { private static boolean monotoneBC = false; /** * Call with parameter describing a permutation, e.g., "2 4 1 3", for computing * the block crossing distance of this permutation. * If called without parameter, all of our gadgets will be checked; this will take * some time * @param args */ public static void main(String[] args) { if (args.length > 0) { printDistance(args[0]); } else { checkClauseGadgetNonMonotone(); checkSmallClauseGadgetNonMonotone(); checkNegatorGadgetNonMonotone(); checkVariableGadgetNonMonotone(); checkClauseGadgetMonotone(); checkSmallClauseGadgetMonotone(); checkNegatorGadgetMonotone(); // needs a lot of RAM (about 5 GB)! checkVariableGadgetMonotone(); } } /** * Breadth-first search in permutation set starting from identity permutation. * if monotoneBC=true monotone block crossings are used. * @param n length of permutations * @param maxdist stop bfs when all permutations of distance <=maxdist are found; * permutations not found so far must have larger distance * @return a HashMap with all the distances for permutations of distance <= maxdist */ private static HashMap bcBFS(int n, int maxdist) { Permutation id = new Permutation(n); for (byte i = 0; i < n; i++) { id.p[i] = (byte) (i + 1); } HashMap perms = new HashMap(); LinkedList queue = new LinkedList(); perms.put(id, (byte) 0); queue.addLast(id); // long time = System.currentTimeMillis(); int distreached = 0; while (!queue.isEmpty()) { Permutation pi = queue.poll(); int dist = perms.get(pi); if (dist > distreached) { distreached = dist; System.out.println("generated up to dist = " + dist); } // status message every second // if (time <= System.currentTimeMillis() - 1000) { // time = System.currentTimeMillis(); // double perc = (double) queue.size() / (double) perms.size(); // perc = ((double) Math.round(perc * 1000)) / 10; // // System.err.println("Permutations in Queue: " + queue.size() // + " / Permutations found: " + perms.size() + " ( " // + perc + "%)"); // } // all permutations of distance <= maxdist found; remaining ones // have larger distance if (dist >= maxdist) { break; } for (int i = 0; i < n - 1; i++) { for (int j = i + 1; j < n; j++) { for (int k = j + 1; k < n + 1; k++) { // BlockMove move = new BlockMove(i, j, k); if (monotoneBC && !isReverseMonotone(pi, i, j, k)) { break; } Permutation p = applyMove(pi, i, j, k); if (!perms.containsKey(p)) { perms.put(p, (byte) (dist + 1)); queue.addLast(p); } } } } } return perms; } /** * Check whether a given block move is "reverse monotone", that is, * whether the block move that transforms the permutation back is monotone. * We need this as our BFS starts at the identity and traverses all edges in * opposite direction * @param p current permutation * @param i * @param j * @param k * @return true iff the mvoe is reverse monotone */ private static boolean isReverseMonotone(Permutation p, int i, int j, int k) { int max1 = p.p[i]; int min2 = p.p[j]; for (int l = i + 1; l < j; l++) { if (p.p[l] > max1) { max1 = p.p[l]; } } for (int l = j + 1; l < k; l++) { if (p.p[l] < min2) { min2 = p.p[l]; } } return (max1 < min2); } /** * Applies a block move on a permutation and outputs the resulting permutation * @param p * @param i * @param j * @param k * @return resulting permutation */ private static Permutation applyMove(Permutation p, int i, int j, int k) { Permutation p2 = new Permutation(p.p.length); int m = 0; for (int l = 0; l < i; l++) { p2.p[m++] = p.p[l]; } for (int l = j; l < k; l++) { p2.p[m++] = p.p[l]; } for (int l = i; l < j; l++) { p2.p[m++] = p.p[l]; } for (int l = k; l < p.p.length; l++) { p2.p[m++] = p.p[l]; } return p2; } /** * read distance from HashMap * @param perms HashMap of distances * @param p input permutation * @param maxdist distance up to which permutations are contained in the HashMap * @return distance of the permutation, or maxdist, if the permutation is not * contained in the HashMap */ private static int getDistance(HashMap perms, Permutation p, int maxdist) { int d = (byte)(maxdist+1); if (perms.containsKey(p)) { d = perms.get(p); } return d; } /** * read distance from HashMap * @param perms HashMap of distances * @param p input permutation * @param maxdist distance up to which permutations are contained in the HashMap * @return distance of the permutation, or maxdist, if the permutation is not * contained in the HashMap */ private static int getDistance(HashMap perms, String p, int maxdist) { return getDistance(perms, permutationFromString(p), maxdist); } /** * get String representing a permutation * @param p * @return */ private static String printPerm(Permutation p) { if (p.p.length == 0) { return ""; } String output = "" + p.p[0]; for (int i = 1; i < p.p.length; i++) { output += " " + p.p[i]; } return output; } /** * Tries to find a 4-tuple of permutations that could be used as a negator given the * precomputed HashMap of distances. * @param n * @param perms precomputed distances */ private static void checkPermutationsForNegator(int n, HashMap perms) { for (Permutation p : perms.keySet()) { for (int i = 0; i < n - 3; i++) { for (int j = i + 2; j < n - 1; j++) { Permutation p2 = copy(p); Permutation p3 = copy(p); Permutation p4 = copy(p); swap(p2, i); swap(p2, j); swap(p3, i); swap(p4, j); int d1 = getDistance(perms, p, n); int d2 = getDistance(perms, p2, n); int d3 = getDistance(perms, p3, n); int d4 = getDistance(perms, p4, n); if (d1 == d2 && d1 < d3 && d1 < d4 && p.get(i) < p.get(i + 1) && p.get(j) < p.get(j + 1)) { // if (d1 == d2 && d1 < d3 && d1 < d4) { System.out.println("found quadruple:"); System.out .println(printPerm(p) + " : " + d1 + " steps"); System.out.println(printPerm(p2) + " : " + d2 + " steps"); System.out.println(printPerm(p3) + " : " + d3 + " steps"); System.out.println(printPerm(p4) + " : " + d4 + " steps"); System.out.println(); } } } } } /** * Tries to find an 8-tuple of permutations that could be used for the variable gadget * given the precomputed HashMap of distances. * @param n * @param perms precomputed distances */ private static void checkPermutationsForVariableGadget(int n, HashMap perms) { for (Permutation p : perms.keySet()) { for (int i = 0; i < n - 5; i++) { for (int j = i + 2; j < n - 3; j++) { for (int k = j + 2; k < n - 1; k++) { Permutation p2 = copy(p); Permutation p3 = copy(p); Permutation p4 = copy(p); Permutation p5 = copy(p); Permutation p6 = copy(p); Permutation p7 = copy(p); Permutation p8 = copy(p); swap(p2, i); swap(p2, j); swap(p2, k); swap(p3, i); swap(p4, j); swap(p5, k); swap(p6,j); swap(p6,k); swap(p7,i); swap(p7,k); swap(p8,i); swap(p8,j); int d1 = getDistance(perms, p, n); int d2 = getDistance(perms, p2, n); int d3 = getDistance(perms, p3, n); int d4 = getDistance(perms, p4, n); int d5 = getDistance(perms, p5, n); int d6 = getDistance(perms, p6, n); int d7 = getDistance(perms, p7, n); int d8 = getDistance(perms, p8, n); if (d1 == d2 && d1 < d3 && d1 < d4 && d1 < d5 && d1 < d6 && d1 < d7 && d1 < d8) { System.out.println("found 8-tuple:"); System.out.println(printPerm(p ) + " : " + d1 + " steps"); System.out.println(printPerm(p2) + " : " + d2 + " steps"); System.out.println(printPerm(p3) + " : " + d3 + " steps"); System.out.println(printPerm(p4) + " : " + d4 + " steps"); System.out.println(printPerm(p5) + " : " + d5 + " steps"); System.out.println(printPerm(p6) + " : " + d6 + " steps"); System.out.println(printPerm(p7) + " : " + d7 + " steps"); System.out.println(printPerm(p8) + " : " + d8 + " steps"); System.out.println(); } } } } } } /** * Tries to find an 8-tuple of permutations that could be used for the clause gadget (3 variables) * given the precomputed HashMap of distances. * @param n * @param perms precomputed distances */ private static void checkPermutationsForClauseGadget(int n, HashMap perms) { for (Permutation p : perms.keySet()) { for (int i = 0; i < n - 5; i++) { if (p.get(i) < p.get(i+1)) { break; } for (int j = i + 2; j < n - 3; j++) { if (p.get(j) < p.get(j+1)) { break; } for (int k = j + 2; k < n - 1; k++) { if (p.get(k) < p.get(k+1)) { break; } Permutation p2 = copy(p); Permutation p3 = copy(p); Permutation p4 = copy(p); Permutation p5 = copy(p); Permutation p6 = copy(p); Permutation p7 = copy(p); Permutation p8 = copy(p); swap(p2, i); swap(p2, j); swap(p2, k); swap(p3, i); swap(p4, j); swap(p5, k); swap(p6,j); swap(p6,k); swap(p7,i); swap(p7,k); swap(p8,i); swap(p8,j); int d1 = getDistance(perms, p, n); int d2 = getDistance(perms, p2, n); int d3 = getDistance(perms, p3, n); int d4 = getDistance(perms, p4, n); int d5 = getDistance(perms, p5, n); int d6 = getDistance(perms, p6, n); int d7 = getDistance(perms, p7, n); int d8 = getDistance(perms, p8, n); if (d1 > d2 && d2 == d3 && d2 == d4 && d2 == d5 && d2 == d6 && d2 == d7 && d2 == d8) { System.out.println("found 8-tuple:"); System.out.println(printPerm(p ) + " : " + d1 + " steps"); System.out.println(printPerm(p2) + " : " + d2 + " steps"); System.out.println(printPerm(p3) + " : " + d3 + " steps"); System.out.println(printPerm(p4) + " : " + d4 + " steps"); System.out.println(printPerm(p5) + " : " + d5 + " steps"); System.out.println(printPerm(p6) + " : " + d6 + " steps"); System.out.println(printPerm(p7) + " : " + d7 + " steps"); System.out.println(printPerm(p8) + " : " + d8 + " steps"); System.out.println(); } } } } } } /** * swap two neighboring positions in permutation * @param pi * @param i */ private static void swap(Permutation pi, int i) { byte temp = pi.get(i); pi.p[i] = pi.get(i+1); pi.p[i+1] = temp; } /** * Reads a permutation from a String. * @param sp * @return */ private static Permutation permutationFromString(String sp) { StringTokenizer t = new StringTokenizer(sp, " "); Permutation pi = new Permutation(t.countTokens()); int i = 0; while (t.hasMoreTokens()) { pi.p[i++] = Byte.parseByte(t.nextToken()); } return pi; } /** * Copy permutation. * @param p * @return */ private static Permutation copy(Permutation p) { Permutation p2 = new Permutation(p.p.length); for (int i = 0; i < p.p.length; i++) { p2.p[i] = p.p[i]; } return p2; } /** * Tries to find permutations for the negator gadget for monotone block crossings. */ private static void findNegatorGadgetMonotone() { monotoneBC = true; // compute distances up to 6 // permutations that are not found have distances >= 7 HashMap distances = bcBFS(10, 6); checkPermutationsForNegator(10, distances); } /** * Tries to find permutations for the variable gadget for monotone block crossings. */ private static void findVariableGadgetMonotone() { monotoneBC = true; // compute distances up to 7 // permutations that are not found have distances >= 8 HashMap distances = bcBFS(11, 7); checkPermutationsForVariableGadget(11, distances); } /** * Tries to find permutations for the clause gadget for monotone block crossings. */ private static void findClauseGadgetMonotone() { monotoneBC = true; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(6, 4); checkPermutationsForClauseGadget(6, distances); } /** * Tries to find permutations for the negator gadget for nonmonotone block crossings. */ private static void findNegatorGadgetNonMonotone() { monotoneBC = false; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(7, 4); checkPermutationsForNegator(7, distances); } /** * Tries to find permutations for the variable gadget for nonmonotone block crossings. */ private static void findVariableGadgetNonMonotone() { monotoneBC = false; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(7, 4); checkPermutationsForVariableGadget(7, distances); } /** * Tries to find permutations for the clause gadget for monotone block crossings. */ private static void findClauseGadgetNonMonotone() { monotoneBC = false; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(6, 4); checkPermutationsForClauseGadget(6, distances); } /** * Checks the distances of the permutations used for our clause gadget * with monotone block crossings. */ private static void checkClauseGadgetMonotone() { monotoneBC = true; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(6, 4); printDistance(distances, "3 1 5 2 6 4", 6); printDistance(distances, "3 1 5 2 4 6", 6); printDistance(distances, "3 1 2 5 6 4", 6); printDistance(distances, "3 1 2 5 4 6", 6); printDistance(distances, "1 3 5 2 6 4", 6); printDistance(distances, "1 3 5 2 4 6", 6); printDistance(distances, "1 3 2 5 6 4", 6); printDistance(distances, "1 3 2 5 4 6", 6); } /** * Checks the distances of the permutations used for the 2-port version * of our clause gadget with monotone block crossings. */ private static void checkSmallClauseGadgetMonotone() { monotoneBC = true; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(4, 2); printDistance(distances, "3 1 4 2", 4); printDistance(distances, "3 1 2 4", 4); printDistance(distances, "1 3 4 2", 4); printDistance(distances, "1 3 2 4", 4); } /** * Checks the distances of the permutations used for our clause gadget * with nonmonotone block crossings. */ private static void checkClauseGadgetNonMonotone() { monotoneBC = false; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(6, 4); printDistance(distances, "3 1 5 2 6 4", 6); printDistance(distances, "3 1 5 2 4 6", 6); printDistance(distances, "3 1 2 5 6 4", 6); printDistance(distances, "3 1 2 5 4 6", 6); printDistance(distances, "1 3 5 2 6 4", 6); printDistance(distances, "1 3 5 2 4 6", 6); printDistance(distances, "1 3 2 5 6 4", 6); printDistance(distances, "1 3 2 5 4 6", 6); } /** * Checks the distances of the permutations used for the 2-port version * of our clause gadget with nonmonotone block crossings. */ private static void checkSmallClauseGadgetNonMonotone() { monotoneBC = false; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(4, 2); printDistance(distances, "3 1 4 2", 4); printDistance(distances, "3 1 2 4", 4); printDistance(distances, "1 3 4 2", 4); printDistance(distances, "1 3 2 4", 4); } /** * Checks the distances of the permutations used for our negator gadget * with monotone block crossings. */ private static void checkNegatorGadgetMonotone() { monotoneBC = true; // compute distances up to 6 // permutations that are not found have distances >= 7 HashMap distances = bcBFS(10, 6); printDistance(distances, "4 8 1 6 9 2 5 10 3 7", 8); printDistance(distances, "4 8 1 9 6 5 2 10 3 7", 8); printDistance(distances, "4 8 1 6 9 5 2 10 3 7", 8); printDistance(distances, "4 8 1 9 6 2 5 10 3 7", 8); } /** * Checks the distances of the permutations used for our negator gadget * with nonmonotone block crossings. */ private static void checkNegatorGadgetNonMonotone() { monotoneBC = false; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(7, 4); printDistance(distances, "3 1 6 4 7 2 5", 6); printDistance(distances, "3 6 1 4 7 5 2", 6); printDistance(distances, "3 1 6 4 7 5 2", 6); printDistance(distances, "3 6 1 4 7 2 5", 6); } /** * Checks the distances of the permutations used for our variable gadget * with monotone block crossings. */ private static void checkVariableGadgetMonotone() { monotoneBC = true; // compute distances up to 7 // permutations that are not found have distances >= 8 HashMap distances = bcBFS(11, 7); printDistance(distances, "5 9 3 7 10 1 6 11 8 2 4", 9); printDistance(distances, "5 9 3 10 7 6 1 11 2 8 4", 9); printDistance(distances, "5 9 3 7 10 1 6 11 2 8 4", 9); printDistance(distances, "5 9 3 7 10 6 1 11 8 2 4", 9); printDistance(distances, "5 9 3 7 10 6 1 11 2 8 4", 9); printDistance(distances, "5 9 3 10 7 1 6 11 8 2 4", 9); printDistance(distances, "5 9 3 10 7 1 6 11 2 8 4", 9); printDistance(distances, "5 9 3 10 7 6 1 11 8 2 4", 9); } /** * Checks the distances of the permutations used for our variable gadget * with nonmonotone block crossings. */ private static void checkVariableGadgetNonMonotone() { monotoneBC = false; // compute distances up to 4 // permutations that are not found have distances >= 5 HashMap distances = bcBFS(7, 4); printDistance(distances, "6 1 4 3 7 5 2", 6); printDistance(distances, "6 4 1 7 3 2 5", 6); printDistance(distances, "6 1 4 3 7 2 5", 6); printDistance(distances, "6 1 4 7 3 5 2", 6); printDistance(distances, "6 1 4 7 3 2 5", 6); printDistance(distances, "6 4 1 3 7 5 2", 6); printDistance(distances, "6 4 1 3 7 2 5", 6); printDistance(distances, "6 4 1 7 3 5 2", 6); } private static void printDistance(HashMap perms, String p, int maxdist) { System.out.println("distance of [" + p + "]: " + getDistance(perms, p, maxdist)); } private static void printDistance(String string) { Permutation pi = permutationFromString(string); // largest distance must be smaller than n HashMap distances = bcBFS(pi.p.length, pi.p.length-2); printDistance(distances, printPerm(pi), pi.p.length-1); } } /** * Representation of a permutation as an array of bytes. * hashCode() is overwritten for ensuring that two objects representing the * same permutation are recognized as equal. * @author Martin Fink * */ class Permutation implements Comparable { public byte[] p; public Permutation(int n) { p = new byte[n]; } public byte get(int i) { return p[i]; } @Override public int hashCode() { int hashCode = 1; for (int i = 0; i < p.length; i++) { hashCode = 31*hashCode + p[i]; } return hashCode; } @Override public boolean equals(Object obj) { if (!(obj instanceof Permutation)) { return false; } Permutation p2 = (Permutation) obj; if (p2.p.length != p.length) { return false; } for (int i = 0; i < p.length; i++) { if (p[i] != p2.p[i]) { return false; } } return true; } @Override public int compareTo(Permutation o) { if (!(o instanceof Permutation)) { return -1; } Permutation p2 = (Permutation) o; if (p2.p.length > p.length) { return -1; } if (p2.p.length < p.length) { return 1; } for (int i = 0; i < p.length; i++) { if (p[i] < p2.p[i]) { return -1; } else if (p[i] > p2.p[i]) { return 1; } } return 0; } } .