Permalink
Browse files

Performance improvements for solutions.

 - Eliminate possibilities by using the known rules.
 - Consider cells with the fewest remaining possibilities
    first.
  • Loading branch information...
1 parent 73fdfd5 commit fbbb97cf81fe75a47caa31f91237187fbd7b8af0 @josephw committed Feb 27, 2011
View
111 src/main/java/org/kafsemo/futoshiki/Possibilities.java
@@ -23,12 +23,12 @@
public class Possibilities extends Grid
{
- final BitSet possibilities;
+ private final BitSet possibilities;
public Possibilities(int length)
{
super(length);
- this.possibilities = new BitSet(length * length * 3);
+ this.possibilities = new BitSet(length * length * length);
}
private Possibilities(int length, BitSet p)
@@ -37,24 +37,55 @@ private Possibilities(int length, BitSet p)
this.possibilities = p;
}
- int bit(boolean isRow, int pos, int value)
+ int bit(int column, int row, int value)
{
- return ((pos - 1) * length) + (value - 1)
- + (isRow ? 0 : length * length);
- }
-
- int usedBit(int column, int row)
- {
- return length * length * 2 + idxInternal(column, row);
+ return (((column - 1) * length) + row - 1) * length + value - 1;
}
public void use(int column, int row, int value)
{
if (value < 1 || value > length)
throw new IllegalArgumentException("Bad cell value " + value);
- possibilities.set(bit(false, column, value));
- possibilities.set(bit(true, row, value));
- possibilities.set(usedBit(column,row));
+
+ for (int c = 1; c <= length; c++) {
+ if (c != column) {
+ possibilities.set(bit(c, row, value));
+ }
+ }
+
+ for (int r = 1; r <= length; r++) {
+ if (r != row) {
+ possibilities.set(bit(column, r, value));
+ }
+ }
+
+ for (int v = 1; v <= length; v++) {
+ if (v != value) {
+ possibilities.set(bit(column, row, v));
+ }
+ }
+ }
+
+ int minPossible(int column, int row)
+ {
+ for (int v = 1; v < length; v++) {
+ if (isPossible(column, row, v)) {
+ return v;
+ }
+ }
+
+ return 0;
+ }
+
+ int maxPossible(int column, int row)
+ {
+ for (int v = length; v >= 1; v--) {
+ if (isPossible(column, row, v)) {
+ return v;
+ }
+ }
+
+ return 0;
}
public void use(Futoshiki f)
@@ -67,13 +98,23 @@ public void use(Futoshiki f)
}
}
}
+
+ for (GtRule r : f.getRules()) {
+ int greatestMoreThan = minPossible(r.getLesserColumn(), r.getLesserRow());
+ for (int v = 1; v <= greatestMoreThan; v++) {
+ possibilities.set(bit(r.getGreaterColumn(), r.getGreaterRow(), v));
+ }
+
+ int leastLessThan = maxPossible(r.getGreaterColumn(), r.getGreaterRow());
+ for (int v = leastLessThan; v <= length; v++) {
+ possibilities.set(bit(r.getLesserColumn(), r.getLesserRow(), v));
+ }
+ }
}
public boolean isPossible(int column, int row, int value)
{
- return !(possibilities.get(usedBit(column, row))
- || possibilities.get(bit(false, column, value))
- || possibilities.get(bit(true, row, value)));
+ return !possibilities.get(bit(column, row, value));
}
public Possibilities clone()
@@ -84,29 +125,33 @@ public Possibilities clone()
public BigInteger size()
{
BigInteger total = BigInteger.ONE;
- int blankCount = 0;
for (int r = 1; r <= length; r++) {
for (int c = 1; c <= length; c++) {
- if (!possibilities.get(usedBit(c,r))) {
- blankCount++;
- int available = 0;
-
- for (int v = 1; v <= length; v++) {
- if (isPossible(c, r, v)) {
- available++;
- }
- }
-
- total = total.multiply(BigInteger.valueOf(available));
- }
+ int available = possibleCount(c, r);
+
+ total = total.multiply(BigInteger.valueOf(available));
}
}
- if (blankCount > 0) {
- return total;
- } else {
- return BigInteger.ZERO;
+ return total;
+ }
+
+ public int possibleCount(int column, int row)
+ {
+ int available = 0;
+
+ for (int v = 1; v <= length; v++) {
+ if (isPossible(column, row, v)) {
+ available++;
+ }
}
+
+ return available;
+ }
+
+ public int possibleCount(CellPos cell)
+ {
+ return possibleCount(cell.column, cell.row);
}
}
View
32 src/main/java/org/kafsemo/futoshiki/Solver.java
@@ -78,6 +78,8 @@ private boolean solve(Futoshiki f, CellPos[] blank, int nb, Possibilities poss,
return target.solution(f);
}
+ blank = moveBlankWithLeastPossibilitiesIntoPlace(blank, nb, poss);
+
CellPos p = blank[nb];
// Generate a collection of possibilities
@@ -120,6 +122,36 @@ private boolean solve(Futoshiki f, CellPos[] blank, int nb, Possibilities poss,
return true;
}
+ private CellPos[] moveBlankWithLeastPossibilitiesIntoPlace(CellPos[] blanks, int p, Possibilities poss)
+ {
+ int fewestIdx = -1;
+
+ for (int i = p; i < blanks.length; i++) {
+ if (fewestIdx >= 0) {
+ int p1 = poss.possibleCount(blanks[fewestIdx]);
+ int p2 = poss.possibleCount(blanks[i]);
+
+ if (p2 < p1) {
+ fewestIdx = i;
+ }
+
+
+ } else {
+ fewestIdx = i;
+ }
+ }
+
+ if (fewestIdx >= 0 && fewestIdx != p) {
+ // Only necessary with multiple threads
+// blanks = blanks.clone();
+ CellPos cp = blanks[p];
+ blanks[p] = blanks[fewestIdx];
+ blanks[fewestIdx] = cp;
+ }
+
+ return blanks;
+ }
+
private static BigInteger sum(BigInteger[] a, int maxIndex)
{
BigInteger total = BigInteger.ZERO;
View
105 src/test/java/org/kafsemo/futoshiki/test/TestPossibilities.java
@@ -23,6 +23,7 @@
import static org.junit.Assert.assertTrue;
import java.math.BigInteger;
+import java.util.TreeSet;
import org.junit.Test;
import org.kafsemo.futoshiki.Futoshiki;
@@ -38,19 +39,19 @@ public void emptyByDefault()
}
@Test
- public void notPossibleWhenUsed()
+ public void stillPossibleWhenUsed()
{
Possibilities p = new Possibilities(1);
p.use(1, 1, 1);
- assertFalse(p.isPossible(1, 1, 1));
+ assertTrue(p.isPossible(1, 1, 1));
}
@Test
public void usedNumberNotAvailableInSameRowOrColumn()
{
Possibilities p = new Possibilities(2);
p.use(1, 1, 1);
- assertFalse(p.isPossible(1, 1, 1));
+ assertTrue(p.isPossible(1, 1, 1));
assertFalse(p.isPossible(1, 2, 1));
assertFalse(p.isPossible(2, 1, 1));
assertTrue(p.isPossible(2, 2, 1));
@@ -92,15 +93,22 @@ public void totalNumberOfPossibilitiesKnown()
p = new Possibilities(3);
assertEquals(BigInteger.valueOf(19683), p.size());
}
-
+
@Test
- public void totalNumberReducedWhenNumbersAreFixed()
+ public void totalPossibilitiesIsConstantForOneByOne()
{
Possibilities p;
p = new Possibilities(1);
+ assertEquals(BigInteger.ONE, p.size());
p.use(1, 1, 1);
- assertEquals(BigInteger.ZERO, p.size());
+ assertEquals(BigInteger.ONE, p.size());
+ }
+
+ @Test
+ public void totalNumberReducedWhenNumbersAreFixed()
+ {
+ Possibilities p;
p = new Possibilities(2);
p.use(1, 1, 1);
@@ -135,4 +143,89 @@ public void useExistingPuzzleToReducePossibilities()
p.use(f);
assertEquals(BigInteger.valueOf(2), p.size());
}
+
+ @Test
+ public void useExistingPuzzleRulesToReducePossibilities()
+ {
+ Possibilities p = new Possibilities(2);
+
+ Futoshiki f = new Futoshiki(2);
+
+ f.addGtRule(1, 1, 2, 1);
+ p.use(f);
+
+ assertFalse(p.isPossible(1, 1, 1));
+ assertTrue(p.isPossible(1, 1, 2));
+
+ assertTrue(p.isPossible(2, 1, 1));
+ assertFalse(p.isPossible(2, 1, 2));
+ }
+
+ @Test
+ public void useExistingPuzzleRulesWithNumbersToReducePossibilities()
+ {
+ Possibilities p = new Possibilities(3);
+
+ Futoshiki f = new Futoshiki(3);
+
+ f.set(1, 1, 2);
+ f.addGtRule(2, 1, 1, 1);
+ p.use(f);
+
+ assertFalse(p.isPossible(2, 1, 1));
+ assertFalse(p.isPossible(2, 1, 2));
+ assertTrue(p.isPossible(2, 1, 3));
+ }
+
+ @Test
+ public void getPossibilityCountForSpecificCell()
+ {
+ Possibilities p;
+
+ p = new Possibilities(1);
+ assertEquals(1, p.possibleCount(1, 1));
+
+ p = new Possibilities(9);
+ assertEquals(9, p.possibleCount(1, 1));
+ }
+
+ @Test
+ public void specificCellPossibilityCountIsReduced()
+ {
+ Possibilities p;
+
+ p = new Possibilities(2);
+ assertEquals(2, p.possibleCount(1, 1));
+
+ p.use(1, 1, 1);
+ assertEquals(1, p.possibleCount(1, 1));
+ assertEquals(1, p.possibleCount(2, 1));
+
+ p = new Possibilities(9);
+ p.use(1, 1, 1);
+ assertEquals(8, p.possibleCount(2, 1));
+ }
+
+ public static String toString(Possibilities p)
+ {
+ StringBuilder sb = new StringBuilder();
+
+ for (int r = 1; r <= p.getLength(); r++) {
+ for (int c = 1; c <= p.getLength(); c++) {
+ TreeSet<Integer> ts = new TreeSet<Integer>();
+ for (int v = 1; v <= p.getLength(); v++) {
+ if (p.isPossible(c, r, v)) {
+ ts.add(v);
+ }
+ }
+
+ sb.append(ts);
+ sb.append(" | ");
+ }
+ sb.append("\n");
+ }
+ sb.append("--\n");
+
+ return sb.toString();
+ }
}

0 comments on commit fbbb97c

Please sign in to comment.