diff --git a/src/main/java/de/neemann/digital/analyse/quinemc/QuineMcCluskey.java b/src/main/java/de/neemann/digital/analyse/quinemc/QuineMcCluskey.java index 722c160f8..46ae92d75 100644 --- a/src/main/java/de/neemann/digital/analyse/quinemc/QuineMcCluskey.java +++ b/src/main/java/de/neemann/digital/analyse/quinemc/QuineMcCluskey.java @@ -32,7 +32,7 @@ public class QuineMcCluskey { this.primes = new ArrayList<>(); } - private QuineMcCluskey(List variables, TableRows rows, ArrayList primes) { + QuineMcCluskey(List variables, TableRows rows, ArrayList primes) { this.variables = variables; this.rows = rows; this.primes = primes; @@ -141,7 +141,7 @@ public class QuineMcCluskey { newRow.setToOptimized(index); TableRow r = newRows.findRow(newRow); - if (r==null) { + if (r == null) { newRow.addSource(r1.getSource()); newRow.addSource(r2.getSource()); newRows.add(newRow); @@ -225,67 +225,64 @@ public class QuineMcCluskey { /** * Simplify the primes * - * @param primeSelector the prome selector to use - * @return this for call chaning + * @param primeSelector the prime selector to use + * @return this for call chaining */ public QuineMcCluskey simplifyPrimes(PrimeSelector primeSelector) { - ArrayList primesAvail = new ArrayList(primes); - primes.clear(); - TreeSet termIndices = new TreeSet<>(); - for (TableRow r : primesAvail) - termIndices.addAll(r.getSource()); + TreeSet columns = new TreeSet<>(); + for (TableRow r : primes) + columns.addAll(r.getSource()); - // Nach primtermen suchen, welche einen index exclusiv enthalten - // Diese müssen in jedem Falle enthalten sein! - for (int pr : termIndices) { + // remove all primes which are easy to remove + while (true) { + // find rows to delete + HashSet rowsToDelete = new HashSet<>(); + for (TableRow r1 : primes) + for (TableRow r2 : primes) { + if ((r1 != r2) && !rowsToDelete.contains(r1) && r1.getSource().containsAll(r2.getSource())) + rowsToDelete.add(r2); + } - TableRow foundPrime = null; - for (TableRow tr : primesAvail) { - if (tr.getSource().contains(pr)) { - if (foundPrime == null) { - foundPrime = tr; - } else { - foundPrime = null; - break; - } + primes.removeAll(rowsToDelete); + + // find the cols to delete + HashSet colsToDelete = new HashSet<>(); + for (int c1 : columns) { + for (int c2 : columns) { + if ((c1 != c2) && !colsToDelete.contains(c1) && smaller(c1, c2, primes)) + colsToDelete.add(c2); } } - if (foundPrime != null) { - if (!primes.contains(foundPrime)) - primes.add(foundPrime); - } - } - primesAvail.removeAll(primes); + if (colsToDelete.isEmpty() && rowsToDelete.isEmpty()) + break; - // Die, Indices die wir schon haben können raus; - for (TableRow pr : primes) { - termIndices.removeAll(pr.getSource()); + for (TableRow p : primes) + p.getSource().removeAll(colsToDelete); + + columns.removeAll(colsToDelete); } - if (!termIndices.isEmpty()) { - - //Die noch übrigen Terme durchsuchen ob sie schon komplett dabei sind; - Iterator it = primesAvail.iterator(); - while (it.hasNext()) { - TableRow tr = it.next(); - boolean needed = false; - for (int i : tr.getSource()) { - if (termIndices.contains(i)) { - needed = true; - break; - } - } - if (!needed) { - it.remove(); - } - } - - primeSelector.select(primes, primesAvail, termIndices); + // try to reduce the number of primes needed + if (primeSelector != null && !columns.isEmpty()) { + ArrayList availPrimes = new ArrayList<>(primes.size()); + availPrimes.addAll(primes); + primes.clear(); + primeSelector.select(primes, availPrimes, columns); + if (primes.size() < availPrimes.size()) + System.out.println("reduced from " + availPrimes.size() + " primes to " + primes.size()+" primes."); } return this; } + private boolean smaller(int c1, int c2, ArrayList primes) { + for (TableRow r : primes) { + Collection s = r.getSource(); + if (s.contains(c1) && !s.contains(c2)) + return false; + } + return true; + } } diff --git a/src/main/java/de/neemann/digital/analyse/quinemc/TableRow.java b/src/main/java/de/neemann/digital/analyse/quinemc/TableRow.java index 89c58c9a0..d9d40c60a 100644 --- a/src/main/java/de/neemann/digital/analyse/quinemc/TableRow.java +++ b/src/main/java/de/neemann/digital/analyse/quinemc/TableRow.java @@ -5,6 +5,7 @@ import de.neemann.digital.analyse.expression.Constant; import de.neemann.digital.analyse.expression.Expression; import de.neemann.digital.analyse.expression.Variable; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.TreeSet; @@ -55,12 +56,24 @@ public final class TableRow implements Comparable { * @param dontCare true if don't care */ public TableRow(int cols, int bitValue, int index, boolean dontCare) { - this(cols); + this(cols, bitValue); if (!dontCare) source.add(index); + } + + /** + * Creates a new row. + * Used only for exact cover tests! + * + * @param cols the number of columns + * @param bitValue the value representing the bits in the row + */ + public TableRow(int cols, int bitValue) { + this(cols); state = Integer.reverse(bitValue) >>> (32 - cols); } + /** * Sets the given index to optimized * @@ -163,6 +176,17 @@ public final class TableRow implements Comparable { source.addAll(s); } + /** + * Adds some sources to this line + * + * @param s the sources to add + * @return this for chained calls + */ + public TableRow addSource(Integer... s) { + addSource(Arrays.asList(s)); + return this; + } + /** * Returns an expression build with the given variables * diff --git a/src/main/java/de/neemann/digital/analyse/quinemc/primeselector/PrimeSelector.java b/src/main/java/de/neemann/digital/analyse/quinemc/primeselector/PrimeSelector.java index a29cb97ad..2d1b20c28 100644 --- a/src/main/java/de/neemann/digital/analyse/quinemc/primeselector/PrimeSelector.java +++ b/src/main/java/de/neemann/digital/analyse/quinemc/primeselector/PrimeSelector.java @@ -17,7 +17,7 @@ public interface PrimeSelector { * Selects the primes to use * * @param primes the list to add the primes to - * @param primesAvail the available promes + * @param primesAvail the available primes * @param termIndices the indices */ void select(ArrayList primes, ArrayList primesAvail, TreeSet termIndices); diff --git a/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyDontCareTest.java b/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyDontCareTest.java index 8e8be8b02..51d7dd8f9 100644 --- a/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyDontCareTest.java +++ b/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyDontCareTest.java @@ -43,12 +43,13 @@ public class QuineMcCluskeyDontCareTest extends TestCase { /** - * for more the 3 variables we only test some random tables + * for more then 3 variables we only test some random tables * * @throws ExpressionException */ public void testRegression() throws ExpressionException { for (int n = 4; n < 8; n++) { + // test some tables with n variables for (int i = 0; i < 200; i++) { performTestCalculationRandom(n); } @@ -63,6 +64,15 @@ public class QuineMcCluskeyDontCareTest extends TestCase { performTestCalculation(n, tab); } + /** + * Generates a expression from the table and then checks if + * the expression reproduces the given table. + * Does not test if the expression is minimal. + * + * @param n the number of variables + * @param tab the truth table + * @throws ExpressionException + */ static private void performTestCalculation(int n, int[] tab) throws ExpressionException { ArrayList v = vars(n); Expression e = new QuineMcCluskey(v) diff --git a/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyExactCover.java b/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyExactCover.java new file mode 100644 index 000000000..231292322 --- /dev/null +++ b/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyExactCover.java @@ -0,0 +1,35 @@ +package de.neemann.digital.analyse.quinemc; + +import de.neemann.digital.analyse.expression.Variable; +import junit.framework.TestCase; + +import java.util.ArrayList; + +/** + * Created by hneemann on 09.03.17. + */ +public class QuineMcCluskeyExactCover extends TestCase { + + + public void testExactCoverLoop() { + ArrayList vars = new ArrayList<>(); + vars.add(new Variable("a")); + vars.add(new Variable("b")); + vars.add(new Variable("c")); + vars.add(new Variable("d")); + ArrayList primes = new ArrayList<>(); + primes.add(new TableRow(9, 0).addSource(1,3,5,7,9)); + primes.add(new TableRow(9, 1).addSource(0,1,2,3,5,6,7,8,9)); + primes.add(new TableRow(9, 2).addSource(0,2,3,5,8,9)); + primes.add(new TableRow(9, 3).addSource(1,2,4,6,8,9)); + primes.add(new TableRow(9, 4).addSource(0,1,4,5,8,9)); + primes.add(new TableRow(9, 5).addSource(2,3,6,8,9)); + primes.add(new TableRow(9, 6).addSource(2,4,5,7,9)); + primes.add(new TableRow(9, 7).addSource(0,1,3,6,7,9)); + + + QuineMcCluskey qmc = new QuineMcCluskey(vars, null, primes); + qmc=qmc.simplifyPrimes(null); + } + +} diff --git a/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyRegressionTest.java b/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyRegressionTest.java index 92e754a24..1bd851290 100644 --- a/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyRegressionTest.java +++ b/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyRegressionTest.java @@ -59,7 +59,7 @@ public class QuineMcCluskeyRegressionTest extends TestCase { } - public static void testRegression(int n, int j) throws Exception, FormatterException { + public static void testRegression(int n, int j) throws Exception { int size = 1 << n; boolean[] table = new boolean[size]; diff --git a/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyTest.java b/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyTest.java index aad0368a5..c6aa6a811 100644 --- a/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyTest.java +++ b/src/test/java/de/neemann/digital/analyse/quinemc/QuineMcCluskeyTest.java @@ -50,7 +50,7 @@ public class QuineMcCluskeyTest extends TestCase { Variable c = new Variable("C"); Variable d = new Variable("D"); - Expression e = or(and(a, and(c, d)), or(and(not(c), not(d)), and(not(b), c))); + Expression e = or(and(a, c, d), and(not(c), not(d)), and(not(b), c)); QuineMcCluskey t = new QuineMcCluskey(e).simplifyStep(); assertFalse(t.isFinished());