ref: 000ebc50785c5c066465fc17668ae64975188d04
parent: 4408476b7584b9a316466fe1bd10867103cddf57
author: Simon Tatham <anakin@pobox.com>
date: Sun Apr 22 09:48:07 EDT 2018
Use the new matching() for latin.c. The new client code is a lot smaller and nicer, because we can throw away the col[] and num[] permutations entirely. Of course, this also means that every puzzle that incorporated latin.c now has to link against matching.c instead of maxflow.c - but since I centralised that secondary dependency into Recipe a few commits ago, it's trivial to switch them all over at once.
--- a/Recipe
+++ b/Recipe
@@ -31,7 +31,7 @@
ALL = list
-LATIN_DEPS = maxflow tree234
+LATIN_DEPS = matching tree234
LATIN = latin LATIN_DEPS
LATIN_SOLVER = latin[STANDALONE_SOLVER] LATIN_DEPS
--- a/latin.c
+++ b/latin.c
@@ -4,7 +4,7 @@
#include "puzzles.h"
#include "tree234.h"
-#include "maxflow.h"
+#include "matching.h"
#ifdef STANDALONE_LATIN_TEST
#define STANDALONE_SOLVER
@@ -1111,11 +1111,11 @@
digit *latin_generate(int o, random_state *rs)
{
digit *sq;
- int *edges, *backedges, *capacity, *flow;
+ int *adjdata, *adjsizes, *matching;
+ int **adjlists;
void *scratch;
- int ne, scratchsize;
int i, j, k;
- digit *row, *col, *numinv, *num;
+ digit *row;
/*
* To efficiently generate a latin square in such a way that
@@ -1128,123 +1128,76 @@
* the theorem guarantees that we will never have to backtrack.
*
* To find a viable row at each stage, we can make use of the
- * support functions in maxflow.c.
+ * support functions in matching.c.
*/
sq = snewn(o*o, digit);
/*
- * In case this method of generation introduces a really subtle
- * top-to-bottom directional bias, we'll generate the rows in
- * random order.
+ * matching.c will take care of randomising the generation of each
+ * row of the square, but in case this entire method of generation
+ * introduces a really subtle top-to-bottom directional bias,
+ * we'll also generate the rows themselves in random order.
*/
row = snewn(o, digit);
- col = snewn(o, digit);
- numinv = snewn(o, digit);
- num = snewn(o, digit);
for (i = 0; i < o; i++)
row[i] = i;
shuffle(row, i, sizeof(*row), rs);
/*
- * Set up the infrastructure for the maxflow algorithm.
+ * Set up the infrastructure for the matching subroutine.
*/
- scratchsize = maxflow_scratch_size(o * 2 + 2);
- scratch = smalloc(scratchsize);
- backedges = snewn(o*o + 2*o, int);
- edges = snewn((o*o + 2*o) * 2, int);
- capacity = snewn(o*o + 2*o, int);
- flow = snewn(o*o + 2*o, int);
- /* Set up the edge array, and the initial capacities. */
- ne = 0;
- for (i = 0; i < o; i++) {
- /* Each LHS vertex is connected to all RHS vertices. */
- for (j = 0; j < o; j++) {
- edges[ne*2] = i;
- edges[ne*2+1] = j+o;
- /* capacity for this edge is set later on */
- ne++;
- }
- }
- for (i = 0; i < o; i++) {
- /* Each RHS vertex is connected to the distinguished sink vertex. */
- edges[ne*2] = i+o;
- edges[ne*2+1] = o*2+1;
- capacity[ne] = 1;
- ne++;
- }
- for (i = 0; i < o; i++) {
- /* And the distinguished source vertex connects to each LHS vertex. */
- edges[ne*2] = o*2;
- edges[ne*2+1] = i;
- capacity[ne] = 1;
- ne++;
- }
- assert(ne == o*o + 2*o);
- /* Now set up backedges. */
- maxflow_setup_backedges(ne, edges, backedges);
-
+ scratch = smalloc(matching_scratch_size(o, o));
+ adjdata = snewn(o*o, int);
+ adjlists = snewn(o, int *);
+ adjsizes = snewn(o, int);
+ matching = snewn(o, int);
+
/*
* Now generate each row of the latin square.
*/
for (i = 0; i < o; i++) {
- /*
- * To prevent maxflow from behaving deterministically, we
- * separately permute the columns and the digits for the
- * purposes of the algorithm, differently for every row.
- */
- for (j = 0; j < o; j++)
- col[j] = num[j] = j;
- shuffle(col, j, sizeof(*col), rs);
- shuffle(num, j, sizeof(*num), rs);
- /* We need the num permutation in both forward and inverse forms. */
- for (j = 0; j < o; j++)
- numinv[num[j]] = j;
+ /*
+ * Make adjacency lists for a bipartite graph joining each
+ * column to all the numbers not yet placed in that column.
+ */
+ for (j = 0; j < o; j++) {
+ int *p, *adj = adjdata + j*o;
+ for (k = 0; k < o; k++)
+ adj[k] = 1;
+ for (k = 0; k < i; k++)
+ adj[sq[row[k]*o + j] - 1] = 0;
+ adjlists[j] = p = adj;
+ for (k = 0; k < o; k++)
+ if (adj[k])
+ *p++ = k;
+ adjsizes[j] = p - adjlists[j];
+ *p = -1;
+ }
/*
- * Set up the capacities for the maxflow run, by examining
- * the existing latin square.
+ * Run the matching algorithm.
*/
- for (j = 0; j < o*o; j++)
- capacity[j] = 1;
- for (j = 0; j < i; j++)
- for (k = 0; k < o; k++) {
- int n = num[sq[row[j]*o + col[k]] - 1];
- capacity[k*o+n] = 0;
- }
-
- /*
- * Run maxflow.
- */
- j = maxflow_with_scratch(scratch, o*2+2, 2*o, 2*o+1, ne,
- edges, backedges, capacity, flow, NULL);
+ j = matching_with_scratch(scratch, o, o, adjlists, adjsizes,
+ rs, matching, NULL);
assert(j == o); /* by the above theorem, this must have succeeded */
/*
- * And examine the flow array to pick out the new row of
- * the latin square.
+ * And use the output to set up the new row of the latin
+ * square.
*/
- for (j = 0; j < o; j++) {
- for (k = 0; k < o; k++) {
- if (flow[j*o+k])
- break;
- }
- assert(k < o);
- sq[row[i]*o + col[j]] = numinv[k] + 1;
- }
+ for (j = 0; j < o; j++)
+ sq[row[i]*o + j] = matching[j] + 1;
}
/*
* Done. Free our internal workspaces...
*/
- sfree(flow);
- sfree(capacity);
- sfree(edges);
- sfree(backedges);
+ sfree(matching);
+ sfree(adjlists);
+ sfree(adjsizes);
+ sfree(adjdata);
sfree(scratch);
- sfree(numinv);
- sfree(num);
- sfree(col);
sfree(row);
/*